Imported Upstream version 2016.6
authorSimon McVittie <smcv@debian.org>
Sun, 26 Jun 2016 11:12:26 +0000 (12:12 +0100)
committerSimon McVittie <smcv@debian.org>
Sun, 26 Jun 2016 11:12:26 +0000 (12:12 +0100)
116 files changed:
Makefile-boot.am
Makefile-decls.am
Makefile-libostree-defines.am
Makefile-libostree.am
Makefile-tests.am
Makefile.am
README.md
apidoc/.gitignore
apidoc/ostree-docs.xml
apidoc/ostree-sections.txt
autogen.sh
configure.ac
docs/manual/atomic-upgrades.md
docs/manual/formats.md
docs/manual/related-projects.md
docs/manual/repo.md
docs/manual/repository-management.md
libglnx/glnx-console.c
libglnx/glnx-console.h
libglnx/glnx-dirfd.c
libglnx/glnx-dirfd.h
libglnx/glnx-fdio.c
libglnx/glnx-fdio.h
libglnx/glnx-libcontainer.c
libglnx/glnx-local-alloc.h
man/ostree-commit.xml
packaging/ostree.spec.in
src/libostree/libostree.sym
src/libostree/ostree-async-progress.c
src/libostree/ostree-autocleanups.h [new file with mode: 0644]
src/libostree/ostree-bootloader-syslinux.c
src/libostree/ostree-bootloader-uboot.c
src/libostree/ostree-core-private.h
src/libostree/ostree-core.c
src/libostree/ostree-core.h
src/libostree/ostree-deployment-private.h
src/libostree/ostree-diff.c
src/libostree/ostree-diff.h
src/libostree/ostree-dummy-enumtypes.c [new file with mode: 0644]
src/libostree/ostree-dummy-enumtypes.h [new file with mode: 0644]
src/libostree/ostree-enumtypes.c.template
src/libostree/ostree-enumtypes.h.template
src/libostree/ostree-fetcher.c
src/libostree/ostree-gpg-verifier.h
src/libostree/ostree-gpg-verify-result.c
src/libostree/ostree-gpg-verify-result.h
src/libostree/ostree-linuxfsutil.c
src/libostree/ostree-lzma-compressor.c
src/libostree/ostree-lzma-decompressor.c
src/libostree/ostree-mutable-tree.c
src/libostree/ostree-mutable-tree.h
src/libostree/ostree-repo-checkout.c
src/libostree/ostree-repo-commit.c
src/libostree/ostree-repo-file.h
src/libostree/ostree-repo-libarchive.c
src/libostree/ostree-repo-private.h
src/libostree/ostree-repo-pull.c
src/libostree/ostree-repo-static-delta-compilation.c
src/libostree/ostree-repo-static-delta-core.c
src/libostree/ostree-repo-traverse.c
src/libostree/ostree-repo.c
src/libostree/ostree-repo.h
src/libostree/ostree-sepolicy.c
src/libostree/ostree-sysroot-cleanup.c
src/libostree/ostree-sysroot-deploy.c
src/libostree/ostree-sysroot-private.h
src/libostree/ostree-sysroot-upgrader.c
src/libostree/ostree-sysroot.c
src/libostree/ostree.h
src/libotutil/ot-fs-utils.c
src/libotutil/ot-fs-utils.h
src/libotutil/ot-gio-utils.c
src/libotutil/ot-variant-utils.c
src/libotutil/ot-variant-utils.h
src/ostree/ot-admin-builtin-init-fs.c
src/ostree/ot-admin-builtin-instutil.c
src/ostree/ot-admin-builtin-switch.c
src/ostree/ot-admin-builtin-upgrade.c
src/ostree/ot-admin-functions.c
src/ostree/ot-admin-instutil-builtin-selinux-ensure-labeled.c
src/ostree/ot-builtin-admin.c
src/ostree/ot-builtin-commit.c
src/ostree/ot-builtin-export.c
src/ostree/ot-builtin-init.c
src/ostree/ot-builtin-pull-local.c
src/ostree/ot-builtin-pull.c
src/ostree/ot-builtin-refs.c
src/ostree/ot-builtin-show.c
src/ostree/ot-builtin-summary.c
src/ostree/ot-builtin-trivial-httpd.c
src/ostree/ot-dump.c
src/ostree/ot-editor.c
src/ostree/ot-main.c
src/ostree/ot-main.h
tests/basic-test.sh
tests/glib.supp [new file with mode: 0644]
tests/libostreetest.c
tests/libostreetest.h
tests/libtest.sh
tests/ostree-valgrind.supp [deleted file]
tests/ostree.supp [new file with mode: 0644]
tests/readdir-rand.c
tests/test-abi.sh [deleted file]
tests/test-admin-deploy-bootid-gc.sh [new file with mode: 0755]
tests/test-basic-c.c
tests/test-checksum.c
tests/test-export.sh
tests/test-libarchive-import.c
tests/test-libarchive.sh
tests/test-parent.sh
tests/test-pull-c.c [new file with mode: 0644]
tests/test-pull-override-url.sh [new file with mode: 0755]
tests/test-refs.sh
tests/test-setuid.sh [deleted file]
tests/test-symbols.sh [new file with mode: 0755]
tests/test-sysroot.js

index 7ec54fa34d023a0968363b11de14991ef7b6e662..8b62d1bae5d04741b93b53265e9de691a890c84c 100644 (file)
@@ -21,7 +21,8 @@ if BUILDOPT_DRACUT
 # Not using $(libdir) here is intentional, dracut modules go in prefix/lib
 dracutmoddir = $(prefix)/lib/dracut/modules.d/98ostree
 dracutmod_SCRIPTS = src/boot/dracut/module-setup.sh
-
+endif
+if BUILDOPT_DRACUT_CONF
 dracutconfdir = $(sysconfdir)/dracut.conf.d
 dracutconf_DATA = src/boot/dracut/ostree.conf
 endif
index d8ec5ab4c345d2d1ef6ca910ebdf723668f08e23..f7ebd422a9ffa38031c2279a729c863022988b69 100644 (file)
@@ -18,7 +18,7 @@
 # Common variables
 AM_CPPFLAGS =
 AM_CFLAGS =
-DISTCHECK_CONFIGURE_FLAGS =
+AM_DISTCHECK_CONFIGURE_FLAGS =
 SUBDIRS =
 NULL = 
 BUILT_SOURCES =
index f623900c37e1e6211cb974dbe64536dbf27fdbcb..2d478bb6c8ed348563af6a026979e306e46942ad 100644 (file)
@@ -21,7 +21,9 @@
 libostree_public_headers = \
        src/libostree/ostree.h \
        src/libostree/ostree-async-progress.h \
+       src/libostree/ostree-autocleanups.h \
        src/libostree/ostree-core.h \
+       src/libostree/ostree-dummy-enumtypes.h \
        src/libostree/ostree-mutable-tree.h \
        src/libostree/ostree-repo.h \
        src/libostree/ostree-types.h \
index a50b2b9d8a3bd5344407238177a1ee48a9316573..d6b8352898eab2e06dc87733378d03512a4bdeb1 100644 (file)
@@ -33,9 +33,11 @@ lib_LTLIBRARIES += libostree-1.la
 libostreeincludedir = $(includedir)/ostree-1
 libostreeinclude_HEADERS = $(libostree_public_headers)
 
-ENUM_TYPES = \
-       $(srcdir)/src/libostree/ostree-fetcher.h \
-       $(NULL)
+ENUM_TYPES = $(NULL)
+
+if USE_LIBSOUP
+ENUM_TYPES += $(srcdir)/src/libostree/ostree-fetcher.h
+endif
 
 src/libostree/ostree-enumtypes.h: src/libostree/ostree-enumtypes.h.template $(ENUM_TYPES)
        $(AM_V_GEN) $(GLIB_MKENUMS) \
@@ -48,12 +50,14 @@ src/libostree/ostree-enumtypes.c: src/libostree/ostree-enumtypes.c.template $(EN
        --fhead "#include \"ostree-enumtypes.h\"" \
        $(ENUM_TYPES) > $@.tmp && mv $@.tmp $@
 
+if USE_LIBSOUP
 ENUM_GENERATED = \
        src/libostree/ostree-enumtypes.h \
        src/libostree/ostree-enumtypes.c \
        $(NULL)
 
 BUILT_SOURCES += $(ENUM_GENERATED)
+endif
 
 CLEANFILES += $(BUILT_SOURCES)
 
@@ -69,6 +73,7 @@ libostree_1_la_SOURCES = \
        src/libostree/ostree-cmdprivate.c \
        src/libostree/ostree-core-private.h \
        src/libostree/ostree-core.c \
+       src/libostree/ostree-dummy-enumtypes.c \
        src/libostree/ostree-checksum-input-stream.c \
        src/libostree/ostree-checksum-input-stream.h \
        src/libostree/ostree-chain-input-stream.c \
@@ -90,6 +95,7 @@ libostree_1_la_SOURCES = \
        src/libostree/ostree-repo.c \
        src/libostree/ostree-repo-checkout.c \
        src/libostree/ostree-repo-commit.c \
+       src/libostree/ostree-repo-pull.c \
        src/libostree/ostree-repo-libarchive.c \
        src/libostree/ostree-repo-prune.c \
        src/libostree/ostree-repo-refs.c \
@@ -123,6 +129,7 @@ libostree_1_la_SOURCES = \
        src/libostree/ostree-gpg-verifier.h \
        src/libostree/ostree-gpg-verify-result.c \
        src/libostree/ostree-gpg-verify-result-private.h \
+       src/libostree/ostree-autocleanups.h \
        $(NULL)
 if USE_LIBARCHIVE
 libostree_1_la_SOURCES += src/libostree/ostree-libarchive-input-stream.h \
@@ -142,6 +149,8 @@ libostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/bsdiff -I$(srcdir)/libglnx -I$(
 libostree_1_la_LDFLAGS = -version-number 1:0:0 -Bsymbolic-functions -Wl,--version-script=$(top_srcdir)/src/libostree/libostree.sym
 libostree_1_la_LIBADD = libotutil.la libbupsplit.la libglnx.la libbsdiff.la libostree-kernel-args.la $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) $(OT_DEP_LZMA_LIBS) $(OT_DEP_ZLIB_LIBS)
 
+EXTRA_DIST += src/libostree/libostree.sym
+
 if USE_LIBARCHIVE
 libostree_1_la_CFLAGS += $(OT_DEP_LIBARCHIVE_CFLAGS)
 libostree_1_la_LIBADD += $(OT_DEP_LIBARCHIVE_LIBS)
@@ -153,7 +162,6 @@ libostree_1_la_SOURCES += \
        src/libostree/ostree-fetcher.c \
        src/libostree/ostree-metalink.h \
        src/libostree/ostree-metalink.c \
-       src/libostree/ostree-repo-pull.c \
        $(NULL)
 libostree_1_la_CFLAGS += $(OT_INTERNAL_SOUP_CFLAGS)
 libostree_1_la_LIBADD += $(OT_INTERNAL_SOUP_LIBS)
index b3d751428b6dc632d6663cdc873709fc0131161b..80903071c5be599a12ef0c5daf6e92c31326f5a7 100644 (file)
 
 include $(top_srcdir)/buildutil/glib-tap.mk
 
+EXTRA_DIST += \
+       buildutil/tap-driver.sh \
+       buildutil/tap-test \
+       $(NULL)
+
 # We should probably consider flipping the default for DEBUG.  Also,
 # include the builddir in $PATH so we find our just-built ostree
 # binary.
 TESTS_ENVIRONMENT += OT_TESTS_DEBUG=1 \
        GI_TYPELIB_PATH=$$(cd $(top_builddir) && pwd) \
        LD_LIBRARY_PATH=$$(cd $(top_builddir)/.libs && pwd) \
-       PATH=$$(cd $(top_builddir) && pwd):$${PATH} \
+       PATH=$$(cd $(top_builddir)/tests && pwd):$${PATH} \
        $(NULL)
 
-uninstalled_test_scripts = tests/test-abi.sh
+uninstalled_test_data = tests/ostree-symlink-stamp
+
+dist_uninstalled_test_scripts = tests/test-symbols.sh
 
-test_scripts = \
+dist_test_scripts = \
        tests/test-basic.sh \
        tests/test-pull-subpath.sh \
        tests/test-archivez.sh \
@@ -51,6 +58,7 @@ test_scripts = \
        tests/test-pull-summary-sigs.sh \
        tests/test-pull-resume.sh \
        tests/test-pull-untrusted.sh \
+       tests/test-pull-override-url.sh \
        tests/test-local-pull.sh \
        tests/test-local-pull-depth.sh \
        tests/test-gpg-signed-commit.sh \
@@ -62,6 +70,7 @@ test_scripts = \
        tests/test-admin-deploy-etcmerge-cornercases.sh \
        tests/test-admin-deploy-uboot.sh \
        tests/test-admin-deploy-grub2.sh \
+       tests/test-admin-deploy-bootid-gc.sh \
        tests/test-admin-instutil-set-kargs.sh \
        tests/test-admin-upgrade-not-backwards.sh \
        tests/test-admin-pull-deploy-commit.sh \
@@ -70,7 +79,6 @@ test_scripts = \
        tests/test-repo-checkout-subpath.sh     \
        tests/test-reset-nonlinear.sh \
        tests/test-oldstyle-partial.sh \
-       tests/test-setuid.sh \
        tests/test-delta.sh \
        tests/test-xattrs.sh \
        tests/test-auto-summary.sh \
@@ -80,15 +88,15 @@ test_scripts = \
        $(NULL)
 
 if BUILDOPT_FUSE
-test_scripts += tests/test-rofiles-fuse.sh
+dist_test_scripts += tests/test-rofiles-fuse.sh
 endif
 
 # This one uses corrupt-repo-ref.js
 if BUILDOPT_GJS
-test_scripts += tests/test-corruption.sh
+dist_test_scripts += tests/test-corruption.sh
 endif
 
-installed_test_data = tests/archive-test.sh \
+dist_installed_test_data = tests/archive-test.sh \
        tests/pull-test.sh \
        tests/libtest.sh \
        tests/admin-test.sh \
@@ -99,29 +107,29 @@ installed_test_data = tests/archive-test.sh \
        tests/pre-endian-deltas-repo-little.tar.xz \
        $(NULL)
 
-test_extra_scripts = tests/bootloader-entries-crosscheck.py \
+dist_test_extra_scripts = tests/bootloader-entries-crosscheck.py \
      tests/ostree-grub-generator
 
 # We can't use nobase_ as we need to strip off the tests/, can't
 # use plain installed_ as we do need the gpghome/ prefix.
 if ENABLE_INSTALLED_TESTS
 gpginsttestdir = $(installed_testdir)/gpghome
-gpginsttest_DATA = tests/gpghome/secring.gpg \
+dist_gpginsttest_DATA = tests/gpghome/secring.gpg \
        tests/gpghome/pubring.gpg \
        tests/gpghome/trustdb.gpg \
        tests/gpghome/key1.asc \
        tests/gpghome/key2.asc \
        tests/gpghome/key3.asc
 gpginsttest_trusteddir = $(installed_testdir)/gpghome/trusted
-gpginsttest_trusted_DATA = tests/gpghome/trusted/pubring.gpg
+dist_gpginsttest_trusted_DATA = tests/gpghome/trusted/pubring.gpg
 
 gpgvinsttestdir = $(installed_testdir)/gpg-verify-data
-gpgvinsttest_DATA = $(addprefix tests/gpg-verify-data/, \
+dist_gpgvinsttest_DATA = $(addprefix tests/gpg-verify-data/, \
        gpg.conf lgpl2 lgpl2.sig pubring.gpg secring.gpg trustdb.gpg)
 endif
 
 if BUILDOPT_GJS
-installed_test_scripts = tests/test-core.js \
+dist_installed_test_scripts = tests/test-core.js \
        tests/test-sizes.js \
        tests/test-sysroot.js \
        $(NULL)
@@ -129,9 +137,12 @@ endif
 
 test_ltlibraries = libreaddir-rand.la
 libreaddir_rand_la_SOURCES = tests/readdir-rand.c
-libreaddir_rand_la_CFLAGS = $(OT_INTERNAL_GIO_UNIX_CFLAGS)
-libreaddir_rand_la_LIBADD = $(OT_INTERNAL_GIO_UNIX_LIBS)
-libreaddir_rand_la_LDFLAGS = -avoid-version
+libreaddir_rand_la_CFLAGS = $(AM_CFLAGS) $(OT_INTERNAL_GIO_UNIX_CFLAGS)
+libreaddir_rand_la_LIBADD = \
+       -ldl \
+       $(OT_INTERNAL_GIO_UNIX_LIBS) \
+       $(NULL)
+libreaddir_rand_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
 if !ENABLE_INSTALLED_TESTS
 libreaddir_rand_la_LDFLAGS += -rpath $(abs_builddir)
 endif
@@ -139,7 +150,7 @@ endif
 test_programs = tests/test-varint tests/test-ot-unix-utils tests/test-bsdiff tests/test-mutable-tree \
        tests/test-keyfile-utils tests/test-ot-opt-utils tests/test-ot-tool-util \
        tests/test-gpg-verify-result tests/test-checksum tests/test-lzma tests/test-rollsum \
-       tests/test-basic-c tests/test-sysroot-c
+       tests/test-basic-c tests/test-sysroot-c tests/test-pull-c
 
 # An interactive tool
 noinst_PROGRAMS += tests/test-rollsum-cli
@@ -176,6 +187,9 @@ tests_test_basic_c_LDADD = $(TESTS_LDADD)
 tests_test_sysroot_c_CFLAGS = $(TESTS_CFLAGS)
 tests_test_sysroot_c_LDADD = $(TESTS_LDADD)
 
+tests_test_pull_c_CFLAGS = $(TESTS_CFLAGS)
+tests_test_pull_c_LDADD = $(TESTS_LDADD)
+
 tests_test_ot_unix_utils_CFLAGS = $(TESTS_CFLAGS)
 tests_test_ot_unix_utils_LDADD = $(TESTS_LDADD)
 
@@ -216,6 +230,7 @@ tests_test_gpg_verify_result_LDADD = $(TESTS_LDADD) $(OT_INTERNAL_GPGME_LIBS)
 
 EXTRA_DIST += \
        tests/libostreetest.h \
+       tests/libtest.sh \
        tests/gpg-verify-data/README.md \
        tests/gpg-verify-data/lgpl2 \
        tests/gpg-verify-data/lgpl2.sig \
@@ -227,7 +242,12 @@ EXTRA_DIST += \
 tests/libreaddir-rand.so: Makefile
        $(AM_V_GEN) ln -fns ../.libs/libreaddir-rand.so tests
 ALL_LOCAL_RULES += tests/libreaddir-rand.so
-CLEANFILES += tests/libreaddir-rand.so
+CLEANFILES += tests/libreaddir-rand.so tests/ostree-symlink-stamp tests/ostree
+
+tests/ostree-symlink-stamp: Makefile
+       @real_bin=`cd $(top_builddir) && libtool --mode=execute echo ostree`; \
+       ln -sf "$${real_bin}" tests/ostree; \
+       touch $@
 
 # Unfortunately the glib test data APIs don't actually handle
 # non-recursive Automake, so we change our code to canonically look
index 488d4b6d8041d0538d892cc800021df9dba31610..1de3154444a470a0db1dd8225f235fb2a1f6220a 100644 (file)
@@ -24,11 +24,12 @@ AM_CPPFLAGS += -DDATADIR='"$(datadir)"' -DLIBEXECDIR='"$(libexecdir)"' \
        -DLOCALEDIR=\"$(datadir)/locale\" -DSYSCONFDIR=\"$(sysconfdir)\" \
        -DSHORTENED_SYSCONFDIR=\"$(shortened_sysconfdir)\" \
        -DOSTREE_FEATURES='"$(OSTREE_FEATURES)"' \
+       -DOSTREE_COMPILATION \
        -DG_LOG_DOMAIN=\"OSTree\" \
        -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_40 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_40 \
        -DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_2_40 -DSOUP_VERSION_MAX_ALLOWED=SOUP_VERSION_2_48
-AM_CFLAGS += $(WARN_CFLAGS)
-DISTCHECK_CONFIGURE_FLAGS += --enable-gtk-doc --disable-maintainer-mode
+AM_CFLAGS += -std=gnu99 $(WARN_CFLAGS)
+AM_DISTCHECK_CONFIGURE_FLAGS += --enable-gtk-doc --disable-maintainer-mode
 
 GITIGNOREFILES = aclocal.m4 build-aux/ buildutil/*.m4 config.h.in gtk-doc.make
 
@@ -71,7 +72,9 @@ include Makefile-otutil.am
 include Makefile-libostree.am
 include Makefile-ostree.am
 include Makefile-switchroot.am
+if BUILDOPT_FUSE
 include src/rofiles-fuse/Makefile-inc.am
+endif
 include Makefile-tests.am
 include Makefile-boot.am
 include Makefile-man.am
index 15cba7ab4693cb9cb3de06839518d95aa373fb63..caa8503d9c157393d4d700ac01659d609c413747 100644 (file)
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@ provide a minimal host for Docker formatted Linux containers.
 Replicating a base immutable OS, then using Docker for applications
 meshes together two different tools with different tradeoffs.
 
-[xdg-app](https://github.com/alexlarsson/xdg-app) uses OSTree
+[flatpak](https://github.com/alexlarsson/xdg-app) uses OSTree
 for desktop application containers.
 
 [GNOME Continuous](https://wiki.gnome.org/Projects/GnomeContinuous) is
index 77cacd4dd91a991ed8efa1b9982bcd23ecb75cf8..80d2c1129ef16ef8b2be4c4940365687e4c40928 100644 (file)
@@ -32,6 +32,7 @@
 /ostree.args
 /ostree.hierarchy
 /ostree.interfaces
+/ostree-overrides.txt
 /ostree.pdf
 /ostree.prerequisites
 /ostree.signals
@@ -48,4 +49,5 @@
 /tmpl/*.bak
 /tmpl/ostree-unused.sgml
 /xml
+/version.xml
 _libs
index c5ea28e6193b78f5e1a7e328b916698e0d077976..8721ffa8348431477bf72d6ebbdc4e2ba13ac0fd 100644 (file)
 
        <chapter xml:id="reference">
                <title>API Reference</title>
-               <xi:include href="xml/libostree-core.xml"/>
-               <xi:include href="xml/libostree-repo.xml"/>
-               <xi:include href="xml/libostree-mutable-tree.xml"/>
-               <xi:include href="xml/libostree-sysroot.xml"/>
-               <xi:include href="xml/libostree-async-progress.xml"/>
-               <xi:include href="xml/libostree-sepolicy.xml"/>
-               <xi:include href="xml/libostree-sysroot-upgrader.xml"/>
-               <xi:include href="xml/libostree-gpg-verify-result.xml"/>
-               <xi:include href="xml/libostree-bootconfig-parser.xml"/>
-               <xi:include href="xml/libostree-chain-input-stream.xml"/>
-               <xi:include href="xml/libostree-checksum-input-stream.xml"/>
-               <xi:include href="xml/libostree-deployment.xml"/>
-               <xi:include href="xml/libostree-diff.xml"/>
-               <xi:include href="xml/libostree-repo-file.xml"/>
-
+               <xi:include href="xml/ostree-core.xml"/>
+               <xi:include href="xml/ostree-repo.xml"/>
+               <xi:include href="xml/ostree-mutable-tree.xml"/>
+               <xi:include href="xml/ostree-sysroot.xml"/>
+               <xi:include href="xml/ostree-async-progress.xml"/>
+               <xi:include href="xml/ostree-sepolicy.xml"/>
+               <xi:include href="xml/ostree-sysroot-upgrader.xml"/>
+               <xi:include href="xml/ostree-gpg-verify-result.xml"/>
+               <xi:include href="xml/ostree-bootconfig-parser.xml"/>
+               <xi:include href="xml/ostree-chain-input-stream.xml"/>
+               <xi:include href="xml/ostree-checksum-input-stream.xml"/>
+               <xi:include href="xml/ostree-deployment.xml"/>
+               <xi:include href="xml/ostree-diff.xml"/>
+               <xi:include href="xml/ostree-repo-file.xml"/>
                <index id="api-index-full">
                        <title>API Index</title>
                        <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
                </index>
-
        </chapter>
 </book>
index 1eef5da7bde6b50e25a7bdcff1af2fde6cb4c2c7..6dca9ecc9ca9b6e7f5d9a68987d1b964dfe9f6d2 100644 (file)
@@ -1,5 +1,5 @@
 <SECTION>
-<FILE>libostree-async-progress</FILE>
+<FILE>ostree-async-progress</FILE>
 OstreeAsyncProgress
 ostree_async_progress_new
 ostree_async_progress_new_and_connect
@@ -22,12 +22,14 @@ ostree_async_progress_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-bootconfig-parser</FILE>
+<FILE>ostree-bootconfig-parser</FILE>
 OstreeBootconfigParser
 ostree_bootconfig_parser_new
 ostree_bootconfig_parser_clone
 ostree_bootconfig_parser_parse
+ostree_bootconfig_parser_parse_at
 ostree_bootconfig_parser_write
+ostree_bootconfig_parser_write_at
 ostree_bootconfig_parser_set
 ostree_bootconfig_parser_get
 <SUBSECTION Standard>
@@ -38,7 +40,7 @@ ostree_bootconfig_parser_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-chain-input-stream</FILE>
+<FILE>ostree-chain-input-stream</FILE>
 OstreeChainInputStream
 ostree_chain_input_stream_new
 <SUBSECTION Standard>
@@ -54,7 +56,7 @@ ostree_chain_input_stream_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-checksum-input-stream</FILE>
+<FILE>ostree-checksum-input-stream</FILE>
 OstreeChecksumInputStream
 ostree_checksum_input_stream_new
 <SUBSECTION Standard>
@@ -70,7 +72,7 @@ ostree_checksum_input_stream_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-core</FILE>
+<FILE>ostree-core</FILE>
 OSTREE_MAX_METADATA_SIZE
 OSTREE_MAX_METADATA_WARN_SIZE
 OSTREE_MAX_RECURSION
@@ -97,10 +99,11 @@ ostree_checksum_inplace_from_bytes
 ostree_checksum_inplace_to_bytes
 ostree_checksum_bytes_peek
 ostree_checksum_bytes_peek_validate
+ostree_checksum_b64_inplace_from_bytes
+ostree_checksum_b64_inplace_to_bytes
 ostree_cmp_checksum_bytes
 ostree_validate_rev
 ostree_parse_refspec
-ostree_checksum_update_meta
 ostree_object_type_to_string
 ostree_object_type_from_string
 ostree_hash_object_name
@@ -110,6 +113,8 @@ ostree_object_to_string
 ostree_object_from_string
 ostree_content_stream_parse
 ostree_content_file_parse
+ostree_content_file_parse_at
+ostree_raw_file_to_archive_z2_stream
 ostree_raw_file_to_content_stream
 ostree_checksum_file_from_input
 ostree_checksum_file
@@ -128,7 +133,7 @@ ostree_commit_get_timestamp
 </SECTION>
 
 <SECTION>
-<FILE>libostree-deployment</FILE>
+<FILE>ostree-deployment</FILE>
 OstreeDeployment
 ostree_deployment_hash
 ostree_deployment_equal
@@ -141,11 +146,14 @@ ostree_deployment_get_bootcsum
 ostree_deployment_get_bootserial
 ostree_deployment_get_bootconfig
 ostree_deployment_get_origin
+ostree_deployment_get_origin_relpath
+ostree_deployment_get_unlocked
 ostree_deployment_set_index
 ostree_deployment_set_bootserial
 ostree_deployment_set_bootconfig
 ostree_deployment_set_origin
 ostree_deployment_clone
+ostree_deployment_unlocked_state_to_string
 <SUBSECTION Standard>
 OSTREE_DEPLOYMENT
 OSTREE_IS_DEPLOYMENT
@@ -154,7 +162,7 @@ ostree_deployment_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-diff</FILE>
+<FILE>ostree-diff</FILE>
 OstreeDiffFlags
 OstreeDiffItem
 ostree_diff_item_ref
@@ -166,7 +174,7 @@ ostree_diff_item_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-gpg-verify-result</FILE>
+<FILE>ostree-gpg-verify-result</FILE>
 OstreeGpgVerifyResult
 OstreeGpgSignatureAttr
 ostree_gpg_verify_result_count_all
@@ -177,6 +185,7 @@ ostree_gpg_verify_result_get_all
 OstreeGpgSignatureFormatFlags
 ostree_gpg_verify_result_describe
 ostree_gpg_verify_result_describe_variant
+ostree_gpg_verify_result_require_valid_signature
 <SUBSECTION Standard>
 OSTREE_GPG_VERIFY_RESULT
 OSTREE_IS_GPG_VERIFY_RESULT
@@ -184,8 +193,33 @@ OSTREE_TYPE_GPG_VERIFY_RESULT
 ostree_gpg_verify_result_get_type
 </SECTION>
 
+<FILE>ostree-lzma-compressor</FILE>
+<SUBSECTION Standard>
+OSTREE_IS_LZMA_COMPRESSOR
+OSTREE_IS_LZMA_COMPRESSOR_CLASS
+OSTREE_LZMA_COMPRESSOR
+OSTREE_LZMA_COMPRESSOR_CLASS
+OSTREE_LZMA_COMPRESSOR_GET_CLASS
+OSTREE_TYPE_LZMA_COMPRESSOR
+OstreeLzmaCompressor
+OstreeLzmaCompressorClass
+</SECTION>
+
+<SECTION>
+<FILE>ostree-lzma-decompressor</FILE>
+<SUBSECTION Standard>
+OSTREE_IS_LZMA_DECOMPRESSOR
+OSTREE_IS_LZMA_DECOMPRESSOR_CLASS
+OSTREE_LZMA_DECOMPRESSOR
+OSTREE_LZMA_DECOMPRESSOR_CLASS
+OSTREE_LZMA_DECOMPRESSOR_GET_CLASS
+OSTREE_TYPE_LZMA_DECOMPRESSOR
+OstreeLzmaDecompressor
+OstreeLzmaDecompressorClass
+</SECTION>
+
 <SECTION>
-<FILE>libostree-mutable-tree</FILE>
+<FILE>ostree-mutable-tree</FILE>
 OstreeMutableTree
 ostree_mutable_tree_new
 ostree_mutable_tree_set_metadata_checksum
@@ -211,7 +245,7 @@ ostree_mutable_tree_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-repo</FILE>
+<FILE>ostree-repo</FILE>
 OstreeRepo
 OstreeRepoMode
 ostree_repo_mode_from_string
@@ -227,6 +261,7 @@ ostree_repo_create
 ostree_repo_get_path
 ostree_repo_get_mode
 ostree_repo_get_config
+ostree_repo_get_dfd
 ostree_repo_copy_config
 ostree_repo_remote_add
 ostree_repo_remote_delete
@@ -238,6 +273,10 @@ ostree_repo_remote_get_gpg_verify
 ostree_repo_remote_get_gpg_verify_summary
 ostree_repo_remote_gpg_import
 ostree_repo_remote_fetch_summary
+ostree_repo_remote_fetch_summary_with_options
+ostree_repo_get_remote_boolean_option
+ostree_repo_get_remote_list_option
+ostree_repo_get_remote_option
 ostree_repo_get_parent
 ostree_repo_write_config
 OstreeRepoTransactionStats
@@ -248,6 +287,8 @@ ostree_repo_abort_transaction
 ostree_repo_transaction_set_refspec
 ostree_repo_transaction_set_ref
 ostree_repo_set_ref_immediate
+ostree_repo_set_cache_dir
+ostree_repo_sign_delta
 ostree_repo_has_object
 ostree_repo_write_metadata
 ostree_repo_write_metadata_async
@@ -270,6 +311,8 @@ ostree_repo_load_object_stream
 ostree_repo_query_object_storage_size
 ostree_repo_import_object_from
 ostree_repo_import_object_from_with_trust
+ostree_repo_import_archive_to_mtree
+ostree_repo_export_tree_to_archive
 ostree_repo_delete_object
 OstreeRepoCommitFilterResult
 OstreeRepoCommitFilter
@@ -285,6 +328,7 @@ ostree_repo_commit_modifier_unref
 ostree_repo_devino_cache_new
 ostree_repo_devino_cache_ref
 ostree_repo_devino_cache_unref
+ostree_repo_devino_cache_get_type
 ostree_repo_write_directory_to_mtree
 ostree_repo_write_dfd_to_mtree
 ostree_repo_write_archive_to_mtree
@@ -310,8 +354,16 @@ ostree_repo_static_delta_execute_offline
 ostree_repo_traverse_new_reachable
 ostree_repo_traverse_commit
 ostree_repo_traverse_commit_union
+ostree_repo_commit_traverse_iter_cleanup
+ostree_repo_commit_traverse_iter_clear
+ostree_repo_commit_traverse_iter_get_dir
+ostree_repo_commit_traverse_iter_get_file
+ostree_repo_commit_traverse_iter_init_commit
+ostree_repo_commit_traverse_iter_init_dirtree
+ostree_repo_commit_traverse_iter_next
 OstreeRepoPruneFlags
 ostree_repo_prune
+ostree_repo_prune_static_deltas
 OstreeRepoPullFlags
 ostree_repo_pull
 ostree_repo_pull_one_dir
@@ -319,6 +371,8 @@ ostree_repo_pull_with_options
 ostree_repo_pull_default_console_progress_changed
 ostree_repo_sign_commit
 ostree_repo_append_gpg_signature
+ostree_repo_add_gpg_signature_summary
+ostree_repo_gpg_verify_data
 ostree_repo_verify_commit
 ostree_repo_verify_commit_ext
 ostree_repo_verify_summary
@@ -333,13 +387,12 @@ ostree_repo_transaction_stats_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-repo-file</FILE>
+<FILE>ostree-repo-file</FILE>
 OstreeRepoFile
 ostree_repo_file_ensure_resolved
 ostree_repo_file_get_xattrs
 ostree_repo_file_get_repo
 ostree_repo_file_get_root
-ostree_repo_file_make_empty_tree
 ostree_repo_file_tree_set_metadata
 ostree_repo_file_tree_get_contents_checksum
 ostree_repo_file_tree_get_metadata_checksum
@@ -360,14 +413,17 @@ ostree_repo_file_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-sepolicy</FILE>
+<FILE>ostree-sepolicy</FILE>
 OstreeSePolicy
 ostree_sepolicy_new
 ostree_sepolicy_get_path
 ostree_sepolicy_get_name
 ostree_sepolicy_get_label
+ostree_sepolicy_get_csum
 OstreeSePolicyRestoreconFlags
 ostree_sepolicy_restorecon
+ostree_sepolicy_setfscreatecon
+ostree_sepolicy_fscreatecon_cleanup
 <SUBSECTION Standard>
 OSTREE_SEPOLICY
 OSTREE_IS_SEPOLICY
@@ -376,7 +432,7 @@ ostree_sepolicy_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-sysroot</FILE>
+<FILE>ostree-sysroot</FILE>
 OstreeSysroot
 ostree_sysroot_new
 ostree_sysroot_new_default
@@ -388,6 +444,7 @@ ostree_sysroot_try_lock
 ostree_sysroot_lock_async
 ostree_sysroot_lock_finish
 ostree_sysroot_unlock
+ostree_sysroot_unload
 ostree_sysroot_get_fd
 ostree_sysroot_ensure_initialized
 ostree_sysroot_get_bootversion
@@ -395,13 +452,17 @@ ostree_sysroot_get_subbootversion
 ostree_sysroot_get_deployments
 ostree_sysroot_get_booted_deployment
 ostree_sysroot_get_deployment_directory
+ostree_sysroot_get_deployment_dirpath
 ostree_sysroot_get_deployment_origin_path
 ostree_sysroot_cleanup
 ostree_sysroot_prepare_cleanup
 ostree_sysroot_get_repo
 ostree_sysroot_init_osname
 ostree_sysroot_deployment_set_kargs
+ostree_sysroot_deployment_set_mutable
+ostree_sysroot_deployment_unlock
 ostree_sysroot_write_deployments
+ostree_sysroot_write_origin_file
 ostree_sysroot_deploy_tree
 ostree_sysroot_get_merge_deployment
 ostree_sysroot_origin_new_from_refspec
@@ -415,7 +476,7 @@ ostree_sysroot_get_type
 </SECTION>
 
 <SECTION>
-<FILE>libostree-sysroot-upgrader</FILE>
+<FILE>ostree-sysroot-upgrader</FILE>
 OstreeSysrootUpgrader
 ostree_sysroot_upgrader_new
 ostree_sysroot_upgrader_new_for_os
index 0eb655502c4152195772e659fab39b3fa91cd17b..581f3dee8fcf06fea8cfc36d9c4e5ef04d915336 100755 (executable)
@@ -33,8 +33,8 @@ if ! test -f libglnx/README.md || ! test -f bsdiff/README.md; then
     git submodule update --init
 fi
 # Workaround automake bug with subdir-objects and computed paths
-sed -e 's,$(libglnx_srcpath),'${srcdir}/libglnx,g < libglnx/Makefile-libglnx.am >libglnx/Makefile-libglnx.am.inc
-sed -e 's,$(libbsdiff_srcpath),'${srcdir}/bsdiff,g < bsdiff/Makefile-bsdiff.am >bsdiff/Makefile-bsdiff.am.inc
+sed -e 's,$(libglnx_srcpath),libglnx,g' < libglnx/Makefile-libglnx.am >libglnx/Makefile-libglnx.am.inc
+sed -e 's,$(libbsdiff_srcpath),bsdiff,g' < bsdiff/Makefile-bsdiff.am >bsdiff/Makefile-bsdiff.am.inc
 
 autoreconf --force --install --verbose
 
index dca9f5363882beaff5a3155ec52ccd9120d97025..18f9f277927070fb1647cb2a9fcefa695df765f6 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ([2.63])
-AC_INIT([ostree], [2016.5], [walters@verbum.org])
+AC_INIT([ostree], [2016.6], [walters@verbum.org])
 AC_CONFIG_HEADER([config.h])
 AC_CONFIG_MACRO_DIR([buildutil])
 AC_CONFIG_AUX_DIR([build-aux])
@@ -38,13 +38,15 @@ GLIB_TESTS
 
 AC_CHECK_HEADER([sys/xattr.h],,[AC_MSG_ERROR([You must have sys/xattr.h from glibc])])
 
-AC_CHECK_PROGS(YACC, 'bison -y', :)
-AS_IF([test "$YACC" = :], [AC_MSG_ERROR([bison not found but required])])
+AS_IF([test "$YACC" != "bison -y"], [AC_MSG_ERROR([bison not found but required])])
 
 PKG_PROG_PKG_CONFIG
 
 AM_PATH_GLIB_2_0
 
+dnl When bumping the gio-unix-2.0 dependency (or glib-2.0 in general),
+dnl remember to bump GLIB_VERSION_MIN_REQUIRED and
+dnl GLIB_VERSION_MAX_ALLOWED in Makefile.am
 GIO_DEPENDENCY="gio-unix-2.0 >= 2.40.0 libgsystem >= 2015.1"
 PKG_CHECK_MODULES(OT_DEP_GIO_UNIX, $GIO_DEPENDENCY)
 
@@ -57,6 +59,9 @@ PKG_CHECK_MODULES(OT_DEP_ZLIB, zlib)
 dnl We're not actually linking to this, just using the header
 PKG_CHECK_MODULES(OT_DEP_E2P, e2p)
 
+dnl When bumping the libsoup-2.4 dependency, remember to bump
+dnl SOUP_VERSION_MIN_REQUIRED and SOUP_VERSION_MAX_ALLOWED in
+dnl Makefile.am
 SOUP_DEPENDENCY="libsoup-2.4 >= 2.39.1"
 AC_ARG_WITH(soup,
            AS_HELP_STRING([--with-soup], [Use libsoup @<:@default=yes@:>@]),
@@ -222,7 +227,7 @@ AC_ARG_ENABLE(rofiles-fuse,
               [AS_HELP_STRING([--enable-rofiles-fuse],
                               [generate rofiles-fuse helper [default=yes]])],,
               enable_rofiles_fuse=yes)
-AS_IF([ test $enable_rofiles_fuse != xno ], [
+AS_IF([ test x$enable_rofiles_fuse != xno ], [
     PKG_CHECK_MODULES(BUILDOPT_FUSE, $FUSE_DEPENDENCY)
 ], [enable_rofiles_fuse=no])
 AM_CONDITIONAL(BUILDOPT_FUSE, test x$enable_rofiles_fuse = xyes)
@@ -231,7 +236,14 @@ AC_ARG_WITH(dracut,
             AS_HELP_STRING([--with-dracut],
                            [Install dracut module (default: no)]),,
               [with_dracut=no])
-AM_CONDITIONAL(BUILDOPT_DRACUT, test x$with_dracut = xyes)
+case x$with_dracut in
+    xno) ;;
+    xyes) ;;
+    xyesbutnoconf) ;;
+    *) AC_MSG_ERROR([Unknown --with-dracut value $with_dracut])
+esac    
+AM_CONDITIONAL(BUILDOPT_DRACUT, test x$with_dracut = xyes || test x$with_dracut = xyesbutnoconf)
+AM_CONDITIONAL(BUILDOPT_DRACUT_CONF, test x$with_dracut = xyes)
 
 AC_ARG_WITH(mkinitcpio,
             AS_HELP_STRING([--with-mkinitcpio],
@@ -239,7 +251,7 @@ AC_ARG_WITH(mkinitcpio,
               [with_mkinitcpio=no])
 AM_CONDITIONAL(BUILDOPT_MKINITCPIO, test x$with_mkinitcpio = xyes)
 
-AS_IF([test "x$with_dracut" = "xyes" || test "x$with_mkinitcpio" = "xyes"], [
+AS_IF([test "x$with_dracut" = "xyes" || test "x$with_dracut" = "xyesbutnoconf" || test "x$with_mkinitcpio" = "xyes"], [
   with_systemd=yes
   AC_ARG_WITH([systemdsystemunitdir],
               AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
index b5f398d632dfa06b802de3f7dcfcf2928f7ba8eb..40515b83d0a2e9283328735c9ad1158ca8f4d8f4 100644 (file)
@@ -45,7 +45,7 @@ operate "live" on the currently booted filesystem.  The way they could
 work with OSTree is instead to take the list of installed packages in
 the currently booted tree, and compute a new filesystem from that.  A
 later chapter describes in more details how this could work:
-[adapting-existing.md](Adapting Existing Systems).
+[Adapting Existing Systems](adapting-existing.md).
 
 For the purposes of this section, let's assume that we have a
 newly generated filesystem tree stored in the repo (which shares
@@ -56,7 +56,7 @@ checking it back out of the repo into a deployment.
 
 Given a commit to deploy, OSTree first allocates a directory for
 it.  This is of the form `/boot/loader/entries/ostree-$osname-$checksum.$serial.conf`.
-The `$serial` is normally 0, but if a
+The `$serial` is normally `0`, but if a
 given commit is deployed more than once, it will be incremented.
 This is supported because the previous deployment may have
 configuration in `/etc` that we do not want to use or overwrite.
index e689f8a8347ec5f5cb61650375d2b28d91df3c0b..87d0005f868e49b6f068804d0407af36b0f28121 100644 (file)
@@ -125,8 +125,7 @@ the client executes.
 
 This "updates as code" model allows for multiple content generation
 strategies.  The design of this was inspired by that of Chromium:
-[http://dev.chromium.org/chromium-os/chromiumos-design-docs/filesystem-autoupdate](ChromiumOS
-autoupdate).
+[ChromiumOS Autoupdate](http://dev.chromium.org/chromium-os/chromiumos-design-docs/filesystem-autoupdate).
 
 ### The delta superblock
 
index 896c7655db33cad9330576d0d91941b66004b69d..d37e2cfecaa97993cfc59b39baf5721a63e1908b 100644 (file)
@@ -78,6 +78,10 @@ available.
 All of the above also applies if one replaces "BTRFS" with "LVM
 snapshots" except for the reflinks.
 
+OSTree supports using "bare-user" repositories, which do not require
+root to use. Using a filesystem-level layer without root is more
+difficult and would likely require a setuid helper or privileged service.
+
 Finally, see the next portion around ChromiumOS for why a hybrid but
 integrated package/image system improves on this.
 
@@ -130,6 +134,11 @@ believes that at the moment, the "CL updater" is not truly atomic in
 the sense that because it applies updates live, there is a window
 where the OS root may be inconsistent.
 
+## Mender.io
+
+[Mender.io](https://mender.io/) is another implementation of the dual
+partition approach.
+
 ## OLPC update
 
 OSTree is basically a generalization of olpc-update, except using
@@ -147,37 +156,54 @@ See
 [this comment](http://blog.verbum.org/2013/08/26/ostree-v2013-6-released/#comment-1169)
 for a comparison.
 
-## NixOS
-
-See [NixOS](http://nixos.org/). It was a very influential project for
-OSTree.  NixOS and OSTree both support the idea of independent "roots"
-that are bootable.
-
-In NixOS, the entire system is based on checksums of package inputs
-(build dependencies) - see [Nix store](http://nixos.org/nix/manual/#chap-package-management/). A both
-positive and negative of the Nix model is that a change in the build
-dependencies (e.g. being built with a newer gcc), requires a cascading
-rebuild of everything.
-
-In OSTree, the checksums are of object *content* (including extended
-attributes). This means that any data that's identical is
-transparently, automatically shared on disk. It's possible to ask the
-Nix store to deduplicate, (via hard links and immutable bit), but this
-is significantly less efficient than the OSTree approach. The Nix use
-of the ext immutable bit is racy, since it has to be briefly removed
-to make a hard link.
-
-At the lowest level, OSTree is just "git for binaries" - it isn't tied
-strongly to any particular build system. You can put whatever data you
-want inside an OSTree repository, built however you like. So for
-example, while one could make a build system that did the "purely
-functional" approach of Nix, it also works to have a build system that
-just rebuilds individual components (packages) as they change, without
-forcing a rebuild of their dependencies.
-
-The author of OSTree believes that while Nix has some good ideas,
-forcing a rebuild of everything for a security update to e.g. glibc is
-not practical at scale.
+## NixOS / Nix
+
+See [NixOS](http://nixos.org/). It was a very influential project for OSTree.
+NixOS and OSTree both support the idea of independent "roots" that are bootable.
+
+In NixOS, files in a package are accessed by a path depending on the checksums
+of package inputs (build dependencies) - see
+[Nix store](http://nixos.org/nix/manual/#chap-package-management/).
+However, OSTree uses a commit/deploy model - it isn't tied to any particular
+directory layout, and you can put whatever data you want inside an OSTree, for
+example the standard FHS layout. A both positive and negative of the Nix model
+is that a change in the build dependencies (e.g. being built with a newer gcc),
+requires a cascading rebuild of everything. It's good because it makes it easy
+to do massive system-wide changes such as gcc upgrades, and allows installing
+multiple versions of packages at once. However, a security update to e.g. glibc
+forces a rebuild of everything from scratch, and so Nix is not practical at
+scale. OSTree supports using a build system that just rebuilds individual
+components (packages) as they change, without forcing a rebuild of their
+dependencies.
+
+Nix automatically detects runtime package dependencies by scanning content for
+hashes. OSTree only supports only system-level images, and doesn't do dependency
+management. Nix can store arbitrary files, using nix-store --add, but, more
+commonly, paths are added as the result of running a derivation file generated
+using the Nix language. OSTree is build-system agnostic; filesystem trees are
+committed using a simple C API, and this is the only way to commit files.
+
+OSTree automatically shares the storage of identical data using hard links into
+a content-addressed store. Nix can deduplicate using hard links as well, using
+the auto-optimise-store option, but this is not on by default, and Nix does not
+guarantee that all of its files are in the content-addressed store. OSTree
+provides a git-like command line interface for browsing the content-addressed
+store, while Nix does not have this functionality.
+
+Nix used to use the immutable bit to prevent modifications to /nix/store, but
+now it uses a read-only bind mount. The bind mount can be privately remounted,
+allowing per-process privileged write access. OSTree uses the immutable
+bit on the root of the deployment, and mounts /usr as read-only.
+
+NixOS supports switching OS images on-the-fly, by maintaining both booted-system
+and current-system roots. It is not clear how well this approach works. OSTree
+currently requries a reboot to switch images.
+
+Finally, NixOS supports installing user-specific packages from trusted
+repositories without requiring root, using a trusted daemon.
+[Flatpak](https://lwn.net/Articles/687909/), based on OSTree, similarly has a
+policykit-based system helper that allows you to authenticate via polkit to
+install into the system repository.
 
 ## Solaris IPS
 
@@ -205,3 +231,40 @@ See
 [bmap](https://source.tizen.org/documentation/reference/bmaptool/introduction).
 A tool for optimized copying of disk images. Intended for offline use,
 so not directly comparable.
+
+## Git
+
+Although OSTree has been called "Git for Binaries", and the two share the idea
+of a hashed content store, the implementation details are quite different.
+OSTree supports extended attributes and uses SHA256 instead of Git's SHA1. It
+"checks out" files via hardlinks, rather than copying, and thus requires the
+checkout to be immutable. At the moment, OSTree commits may have at most one
+parent, as opposed to Git which allows an arbitrary number. Git uses a
+smart-delta protocol for updates, while OSTree uses 1 HTTP request per changed
+file, or can generate static deltas.
+
+## Conda
+
+[Conda](http://conda.pydata.org/docs/) is an "OS-agnostic, system-level binary
+package manager and ecosystem"; although most well-known for its accompanying
+Python distribution anaconda, its scope has been expanding quickly. The package
+format is very similar to well-known ones such as RPM. However, unlike typical
+RPMs, the packages are built to be relocatable. Also, the package manager runs
+natively on Windows. Conda's main advantage is its ability to install
+collections of packages into "environments" by unpacking them all to the same
+directory. Conda reduces duplication across environments using hardlinks,
+similar to OSTree's sharing between deployments (although Conda uses package /
+file path instead of file hash). Overall, it is quite similar to rpm-ostree in
+functionality and scope.
+
+## rpm-ostree
+
+This builds on top of ostree to support building RPMs into OSTree images, and
+even composing RPMs on-the-fly using an overlay filesystem. It is being
+developed by Fedora, Red Hat, and CentOS as part of Project Atomic.
+
+## GNOME Continuous
+
+This is a service that incrementally rebuilds and tests GNOME on every commit.
+The need to make and distribute snapshots for this system was the original
+inspiration for ostree.
index d3be549cb1305b755431df5d7a011f21f44f9cbb..bce7e0c9c7e3ce61f1e36f548a3c3f820a2f30c8 100644 (file)
@@ -47,6 +47,22 @@ payload sections.  The header contains uid, gid, mode, and symbolic
 link target (for symlinks), as well as extended attributes.  After the
 header, for regular files, the content follows.
 
+The OSTree data format intentionally does not contain timestamps. The reasoning
+is that data files may be downloaded at different times, and by different build
+systems, and so will have different timestamps but identical physical content.
+These files may be large, so most users would like them to be shared, both in
+the repository and between the repository and deployments.
+
+This could cause problems with programs that check if files are out-of-date by
+comparing timestamps. For Git, the logical choice is to not mess with
+timestamps, because unnecessary rebuilding is better than a broken tree.
+However, OSTree has to hardlink files to check them out, and commits are assumed
+to be internally consistent with no build steps needed. For this reason, OSTree
+acts as though all timestamps are set to time_t 1, so that comparisons will be
+considered up-to-date. 1 is a better choice than 0 because some programs use 0
+as a special value; for example, GNU Tar warns of an "implausibly old time
+stamp" with 0.
+
 # Repository types and locations
 
 Also unlike git, an OSTree repository can be in one of three separate
@@ -98,7 +114,7 @@ that.
 A later addition to OSTree is the concept of a "summary" file, created
 via the `ostree summary -u` command.  This was introduced for a few
 reasons.  A primary use case is to be a target a
-(Metalink)[https://en.wikipedia.org/wiki/Metalink], which requires a
+[Metalink](https://en.wikipedia.org/wiki/Metalink), which requires a
 single file with a known checksum as a target.
 
 The summary file primarily contains two mappings:
index b83f6c152fa6ca8c2ede79807f32dedc4399a176..b6da96298a9c7a71109782c487320d928fc153f4 100644 (file)
@@ -4,11 +4,12 @@ Once you have a build system going, if you actually want client
 systems to retrieve the content, you will quickly feel a need for
 "repository management".
 
-OSTree itself does not currently come with tools to do this.  One
-reason is that how content is delivered and managed has concerns very
-specific to the organization.  For example, some operating system
-content vendors may want integration with a specific errata
-notification system.
+The command line tool `ostree` does cover some core functionality, but
+doesn't include very high level workflows.  One reason is that how
+content is delivered and managed has concerns very specific to the
+organization.  For example, some operating system content vendors may
+want integration with a specific errata notification system when
+generating commits.
 
 In this section, we will describe some high level ideas and methods
 for managing content in OSTree repositories, mostly independent of any
@@ -21,6 +22,27 @@ repositories today is the [Pulp Project](http://www.pulpproject.org/),
 which has a
 [Pulp OSTree plugin](https://pulp-ostree.readthedocs.org/en/latest/).
 
+## Mirroring repositories
+
+It's very common to want to perform a full or partial mirror, in
+particular across organizational boundaries (e.g. an upstream OS
+provider, and a user that wants offline and faster access to the
+content).  OSTree supports both full and partial mirroring of the base
+`archive-z2` content, although not yet of static deltas.
+
+To create a mirror, first create an `archive-z2` repository (you don't
+need to run this as root), then add the upstream as a remote, then use
+`pull --mirror`.
+
+```
+ostree --repo=repo init --mode=archive-z2
+ostree --repo=repo remote add exampleos https://exampleos.com/ostree/repo
+ostree --repo=repo pull --mirror exampleos:exampleos/x86_64/standard
+```
+
+You can use the `--depth=-1` option to retrieve all history, or a
+positive integer like `3` to retrieve just the last 3 commits.
+
 ## Separate development vs release repositories
 
 By default, OSTree accumulates server side history.  This is actually
index 397331186213ecbb5a1acd17a4c9a847d4a407e6..d40702edfa73a52e76ee2852d5995be69d0df054 100644 (file)
@@ -177,22 +177,9 @@ printpad (const char *padbuf,
   fwrite (padbuf, 1, r, stdout);
 }
 
-/**
- * glnx_console_progress_text_percent:
- * @text: Show this text before the progress bar
- * @percentage: An integer in the range of 0 to 100
- *
- * On a tty, print to the console @text followed by an ASCII art
- * progress bar whose percentage is @percentage.  If stdout is not a
- * tty, a more basic line by line change will be printed.
- *
- * You must have called glnx_console_lock() before invoking this
- * function.
- *
- */
-void
-glnx_console_progress_text_percent (const char *text,
-                                    guint percentage)
+static void
+text_percent_internal (const char *text,
+                       int percentage)
 {
   static const char equals[] = "====================";
   const guint n_equals = sizeof (equals) - 1;
@@ -201,10 +188,6 @@ glnx_console_progress_text_percent (const char *text,
   const guint ncolumns = glnx_console_columns ();
   const guint bar_min = 10;
   const guint input_textlen = text ? strlen (text) : 0;
-  guint textlen;
-  guint barlen;
-
-  g_return_if_fail (percentage >= 0 && percentage <= 100);
 
   if (text && !*text)
     text = NULL;
@@ -236,36 +219,69 @@ glnx_console_progress_text_percent (const char *text,
     (void) fwrite (beginbuf, 1, sizeof (beginbuf), stdout);
   }
 
-  textlen = MIN (input_textlen, ncolumns - bar_min);
-  barlen = ncolumns - textlen;
-
-  if (textlen > 0)
+  if (percentage == -1)
     {
-      fwrite (text, 1, textlen, stdout);
-      fputc (' ', stdout);
+      const guint spacelen = ncolumns - input_textlen;
+      fwrite (text, 1, input_textlen, stdout);
+      printpad (spaces, n_spaces, spacelen);
     }
-  
-  { 
-    const guint nbraces = 2;
-    const guint textpercent_len = 5;
-    const guint bar_internal_len = barlen - nbraces - textpercent_len;
-    const guint eqlen = bar_internal_len * (percentage / 100.0);
-    const guint spacelen = bar_internal_len - eqlen; 
-
-    fputc ('[', stdout);
-    printpad (equals, n_equals, eqlen);
-    printpad (spaces, n_spaces, spacelen);
-    fputc (']', stdout);
-    fprintf (stdout, " %3d%%", percentage);
-  }
+  else
+    {
+      const guint textlen = MIN (input_textlen, ncolumns - bar_min);
+      const guint barlen = ncolumns - (textlen + 1);;
 
-  { const guint spacelen = ncolumns - textlen - barlen;
-    printpad (spaces, n_spaces, spacelen);
-  }
+      if (textlen > 0)
+        {
+          fwrite (text, 1, textlen, stdout);
+          fputc (' ', stdout);
+        }
+
+      {
+        const guint nbraces = 2;
+        const guint textpercent_len = 5;
+        const guint bar_internal_len = barlen - nbraces - textpercent_len;
+        const guint eqlen = bar_internal_len * (percentage / 100.0);
+        const guint spacelen = bar_internal_len - eqlen; 
+
+        fputc ('[', stdout);
+        printpad (equals, n_equals, eqlen);
+        printpad (spaces, n_spaces, spacelen);
+        fputc (']', stdout);
+        fprintf (stdout, " %3d%%", percentage);
+      }
+    }
 
   fflush (stdout);
 }
 
+/**
+ * glnx_console_progress_text_percent:
+ * @text: Show this text before the progress bar
+ * @percentage: An integer in the range of 0 to 100
+ *
+ * On a tty, print to the console @text followed by an ASCII art
+ * progress bar whose percentage is @percentage.  If stdout is not a
+ * tty, a more basic line by line change will be printed.
+ *
+ * You must have called glnx_console_lock() before invoking this
+ * function.
+ *
+ */
+void
+glnx_console_progress_text_percent (const char *text,
+                                    guint percentage)
+{
+  g_return_if_fail (percentage >= 0 && percentage <= 100);
+
+  text_percent_internal (text, percentage);
+}
+
+void
+glnx_console_text (const char *text)
+{
+  text_percent_internal (text, -1);
+}
+
 /**
  * glnx_console_unlock:
  *
index 8fc3865672f136488e45d0581f6580d62dbc8618..8c1d811598bc7b5a0aece5c5a5db3309f315bd34 100644 (file)
@@ -33,6 +33,8 @@ typedef struct GLnxConsoleRef GLnxConsoleRef;
 
 void    glnx_console_lock (GLnxConsoleRef *ref);
 
+void    glnx_console_text (const char     *text);
+
 void    glnx_console_progress_text_percent (const char     *text,
                                              guint           percentage);
 
index 4861ccfeffae512a89da182967034c9b9134fc6e..95d7fe171ccfbe7d97e366d6d55f48ebef48dada 100644 (file)
@@ -277,20 +277,17 @@ glnx_fdrel_abspath (int         dfd,
 }
 
 /**
- * glnx_mkdtempat:
- * @dfd: Directory fd
- * @tmpl: (type filename): template directory name
- * @mode: permissions to create the temporary directory with
- * @error: Error
+ * glnx_gen_temp_name:
+ * @tmpl: (type filename): template directory name, the last 6 characters will be replaced
  *
- * Similar to g_mkdtemp_full, but using openat.
+ * Replace the last 6 characters of @tmpl with random ASCII.  You must
+ * use this in combination with a mechanism to ensure race-free file
+ * creation such as `O_EXCL`.
  */
-gboolean
-glnx_mkdtempat (int dfd,
-                gchar *tmpl,
-                int mode,
-                GError **error)
+void
+glnx_gen_temp_name (gchar *tmpl)
 {
+  size_t len;
   char *XXXXXX;
   int count;
   static const char letters[] =
@@ -300,17 +297,11 @@ glnx_mkdtempat (int dfd,
   GTimeVal tv;
   static int counter = 0;
 
-  g_return_val_if_fail (tmpl != NULL, -1);
+  g_return_if_fail (tmpl != NULL);
+  len = strlen (tmpl);
+  g_return_if_fail (len >= 6);
 
-  /* find the last occurrence of "XXXXXX" */
-  XXXXXX = g_strrstr (tmpl, "XXXXXX");
-
-  if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6))
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
-                   "Invalid temporary directory template '%s'", tmpl);
-      return FALSE;
-    }
+  XXXXXX = tmpl + (len - 6);
 
   /* Get some more or less random data.  */
   g_get_current_time (&tv);
@@ -332,6 +323,31 @@ glnx_mkdtempat (int dfd,
       XXXXXX[4] = letters[v % NLETTERS];
       v /= NLETTERS;
       XXXXXX[5] = letters[v % NLETTERS];
+    }
+}
+
+/**
+ * glnx_mkdtempat:
+ * @dfd: Directory fd
+ * @tmpl: (type filename): template directory name, last 6 characters will be replaced
+ * @mode: permissions to create the temporary directory with
+ * @error: Error
+ *
+ * Similar to g_mkdtemp_full, but using openat.
+ */
+gboolean
+glnx_mkdtempat (int dfd,
+                gchar *tmpl,
+                int mode,
+                GError **error)
+{
+  int count;
+
+  g_return_val_if_fail (tmpl != NULL, -1);
+
+  for (count = 0; count < 100; count++)
+    {
+      glnx_gen_temp_name (tmpl);
 
       if (mkdirat (dfd, tmpl, mode) == -1)
         {
index c13e3dc5d9d7caaff67092db8130cccbbae15ffb..3a7669527e0241a161e9bf60655c72ba778c9710 100644 (file)
@@ -81,6 +81,8 @@ gboolean glnx_opendirat (int             dfd,
 char *glnx_fdrel_abspath (int         dfd,
                           const char *path);
 
+void glnx_gen_temp_name (gchar *tmpl);
+
 gboolean glnx_mkdtempat (int dfd,
                          gchar *tmpl,
                          int mode,
index 466cbc4e82e472e230b3739b5bcee86776865eee..cdbb69fe1abb8c460c99240d87ffc0b4233ac4f0 100644 (file)
@@ -746,5 +746,35 @@ glnx_file_replace_contents_with_perms_at (int                   dfd,
 
   ret = TRUE;
  out:
+  if (!ret)
+    (void) unlink (tmppath);
   return ret;
 }
+
+/**
+ * glnx_stream_fstat:
+ * @stream: A stream containing a Unix file descriptor
+ * @stbuf: Memory location to write stat buffer
+ * @error:
+ *
+ * Some streams created via libgsystem are #GUnixInputStream; these do
+ * not support e.g. g_file_input_stream_query_info().  This function
+ * allows dropping to the raw unix fstat() call for these types of
+ * streams, while still conveniently wrapped with the normal GLib
+ * handling of @error.
+ */
+gboolean
+glnx_stream_fstat (GFileDescriptorBased *stream,
+                   struct stat          *stbuf,
+                   GError              **error)
+{
+  int fd = g_file_descriptor_based_get_fd (stream);
+
+  if (fstat (fd, stbuf) == -1)
+    {
+      glnx_set_prefix_error_from_errno (error, "%s", "fstat");
+      return FALSE;
+    }
+
+  return TRUE;
+}
index c0fd4e4832070f8e89b4310356baf4b18dcf9664..3ca1a660a8954bedeec46d267a62bc55793e5298 100644 (file)
@@ -21,6 +21,7 @@
 #pragma once
 
 #include <glnx-backport-autocleanups.h>
+#include <gio/gfiledescriptorbased.h>
 #include <limits.h>
 #include <dirent.h>
 #include <sys/stat.h>
@@ -121,4 +122,9 @@ glnx_file_copy_at (int                   src_dfd,
                    GCancellable         *cancellable,
                    GError              **error);
 
+gboolean
+glnx_stream_fstat (GFileDescriptorBased *stream,
+                   struct stat          *stbuf,
+                   GError              **error);
+
 G_END_DECLS
index 8c0f340710eda30fab6e0778b75fb94ef9f6a863..38c1937f969e8de597ba388e8354f047feba02b1 100644 (file)
@@ -274,6 +274,14 @@ glnx_libcontainer_run_chroot_private (const char  *dest,
   if (chdir ("/") != 0)
     _perror_fatal ("chdir: ");
 
+  /* Environment variables like PATH in the end are distribution
+   * specific.  The most correct thing would be to run through PAM,
+   * but that's a huge level of pain.  We'd like to drive towards a
+   * standard /usr/bin (i.e. unified sbin too), but for now this is
+   * pretty compatible.
+   */
+  setenv ("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);
+
   if (binary[0] == '/')
     {
       if (execv (binary, argv) != 0)
@@ -281,9 +289,6 @@ glnx_libcontainer_run_chroot_private (const char  *dest,
     }
   else
     {
-      /* Set PATH to something sane. */
-      setenv ("PATH", "/usr/sbin:/usr/bin", 1);
-
       if (execvp (binary, argv) != 0)
         _perror_fatal ("execvp: ");
     }
index af5af4b88ff812555f697773cc503f077a04ffb3..f628b61c9b85d8eaba1d5b218a7f2c721e22700e 100644 (file)
@@ -21,6 +21,7 @@
 #pragma once
 
 #include <gio/gio.h>
+#include <errno.h>
 
 G_BEGIN_DECLS
 
@@ -195,13 +196,17 @@ GLNX_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, glnx_local_keyfile_unref, g_key_file_un
 static inline void
 glnx_cleanup_close_fdp (int *fdp)
 {
-  int fd;
+  int fd, errsv;
 
   g_assert (fdp);
   
   fd = *fdp;
   if (fd != -1)
-    (void) close (fd);
+    {
+      errsv = errno;
+      (void) close (fd);
+      errno = errsv;
+    }
 }
 
 /**
index 4a86c1d2cc6c00bfad4ef63e838c38e2aa4c0947..8f0037f6a942af0ed9a34f3547351f56063de4a9 100644 (file)
@@ -57,7 +57,7 @@ Boston, MA 02111-1307, USA.
         <title>Description</title>
 
         <para>
-            This allows you to commit changes to a branch.  The specification of the branch is required.  If no commit message is specified with <option>--subject</option> then a text editor will be opened.  The commit will be aborted if the commit subject is left empty.  The command will print the checksum of a successful commit.
+            This allows you to commit changes to a branch.  The specification of the branch is required.  The command will print the checksum of a successful commit.
         </para>
     </refsect1>
 
@@ -68,7 +68,7 @@ Boston, MA 02111-1307, USA.
                 <term><option>--subject</option>, <option>-s</option>="SUBJECT"</term>
 
                 <listitem><para>
-                    One line subject.
+                    One line subject. (optional)
                 </para></listitem>
             </varlistentry>
 
@@ -76,7 +76,15 @@ Boston, MA 02111-1307, USA.
                 <term><option>--body</option>, <option>-m</option>="BODY"</term>
 
                 <listitem><para>
-                    Full description.
+                    Full description. (optional)
+                </para></listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term><option>--editor</option>, <option>-e</option></term>
+
+                <listitem><para>
+                    Open a text editor for the commit description.  It will use OSTREE_EDITOR, VISUAL, EDITOR, or vi, in descending order of preference.  The commit will be aborted if the message is left empty.
                 </para></listitem>
             </varlistentry>
 
@@ -84,7 +92,7 @@ Boston, MA 02111-1307, USA.
                 <term><option>--branch</option>, <option>-b</option>="BRANCH"</term>
 
                 <listitem><para>
-                    Branch.
+                    Branch.  Required, unless --orphan is given.
                 </para></listitem>
             </varlistentry>
 
@@ -163,7 +171,15 @@ Boston, MA 02111-1307, USA.
                 <term><option>--statoverride</option>="PATH"</term>
 
                 <listitem><para>
-                    File containing list of modifications to make permissions.
+                    File containing list of modifications to make permissions (file mode, followed by space, followed by file path).
+                </para></listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term><option>--skip-list</option>="PATH"</term>
+
+                <listitem><para>
+                    File containing list of file paths to skip (one path per line).
                 </para></listitem>
             </varlistentry>
 
@@ -206,6 +222,22 @@ Boston, MA 02111-1307, USA.
                     Override the timestamp of the commit to TIMESTAMP.
                 </para></listitem>
             </varlistentry>
+
+            <varlistentry>
+                <term><option>--orphan</option></term>
+
+                <listitem><para>
+                    Create a commit without writing to a ref (branch)
+                </para></listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term><option>--fsync</option>="POLICY"</term>
+
+                <listitem><para>
+                    POLICY is a boolean which specifies whether fsync should be used or not.  Default to true.
+                </para></listitem>
+            </varlistentry>
         </variablelist>
     </refsect1>
 
index 2d6a266043df3d65f3c1e7fb09dfd55cea60cd9c..2f07f0a8df9a6f4697b1ebe2f49aababb149ef70 100644 (file)
@@ -72,7 +72,7 @@ env NOCONFIGURE=1 ./autogen.sh
 %configure --disable-silent-rules \
           --enable-gtk-doc \
           --with-selinux \
-          --with-dracut
+          --with-dracut=yesbutnoconf
 make %{?_smp_mflags}
 
 %install
@@ -94,7 +94,6 @@ rm -rf $RPM_BUILD_ROOT
 %{_bindir}/ostree
 %{_sbindir}/ostree-prepare-root
 %{_sbindir}/ostree-remount
-%{_sysconfdir}/dracut.conf.d/ostree.conf
 %dir %{_prefix}/lib/dracut/modules.d/98ostree
 %{_prefix}/lib/systemd/system/ostree*.service
 %{_prefix}/lib/dracut/modules.d/98ostree/*
index 89a1457ec12d8e7976a3c173e1af87ca7b2e2e59..e8cca9d8d27db9d5dc704eaa86b4031916117ccd 100644 (file)
@@ -323,11 +323,6 @@ global:
         ostree_deployment_unlocked_state_to_string;
 } LIBOSTREE_2016.3;
 
-/*                         NOTE NOTE NOTE
- * Versions above here are released.  Only add symbols below this line.
- *                         NOTE NOTE NOTE
- */
-
 LIBOSTREE_2016.5 {
 global:
         ostree_repo_import_object_from_with_trust;
@@ -337,3 +332,24 @@ global:
         ostree_repo_get_remote_boolean_option;
         ostree_repo_set_cache_dir;
 } LIBOSTREE_2016.4;
+
+LIBOSTREE_2016.6 {
+global:
+        ostree_gpg_verify_result_require_valid_signature;
+        ostree_raw_file_to_archive_z2_stream;
+        ostree_repo_gpg_verify_data;
+        ostree_repo_remote_fetch_summary_with_options;
+} LIBOSTREE_2016.5;
+
+/*                         NOTE NOTE NOTE
+ * Versions above here are released.  Only add symbols below this line.
+ *                         NOTE NOTE NOTE
+ */
+
+/* Uncomment this when adding a new symbol */
+/*
+LIBOSTREE_2016.7 {
+global:
+        ostree_some_new_symbol;
+} LIBOSTREE_2016.6;
+*/
index 59656fc9b01893f35e01d2d0857b472ab00a5001..0851fd8673521347abdb05621fc3af9511f9ffd9 100644 (file)
@@ -23,7 +23,7 @@
 #include "ostree-async-progress.h"
 
 /**
- * SECTION:libostree-async-progress
+ * SECTION:ostree-async-progress
  * @title: Progress notification system for asynchronous operations
  * @short_description: Values representing progress
  *
diff --git a/src/libostree/ostree-autocleanups.h b/src/libostree/ostree-autocleanups.h
new file mode 100644 (file)
index 0000000..7301ef1
--- /dev/null
@@ -0,0 +1,68 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2016 Endless Mobile, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Krzesimir Nowak <krzesimir@kinvolk.io>
+ */
+
+#pragma once
+
+#include <ostree.h>
+
+G_BEGIN_DECLS
+
+#ifndef OSTREE_WITH_AUTOCLEANUPS
+#define OSTREE_WITH_AUTOCLEANUPS 0
+#endif
+
+/* ostree can use g_autoptr backports from libglnx when glib is too
+ * old, but still avoid exposing them to users that also have an old
+ * glib */
+#if defined(OSTREE_COMPILATION) || (OSTREE_WITH_AUTOCLEANUPS && GLIB_CHECK_VERSION(2, 44, 0))
+
+/*
+ * The following types have no specific clear/free/unref functions, so
+ * they can be used as the stack-allocated variables or as the
+ * g_autofree heap-allocated variables.
+ *
+ * OstreeRepoTransactionStats
+ * OstreeRepoImportArchiveOptions
+ * OstreeRepoExportArchiveOptions
+ * OstreeRepoCheckoutOptions
+ */
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeDiffItem, ostree_diff_item_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoCommitModifier, ostree_repo_commit_modifier_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoDevInoCache, ostree_repo_devino_cache_unref)
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeAsyncProgress, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeBootconfigParser, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeDeployment, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeGpgVerifyResult, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeMutableTree, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepo, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFile, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSePolicy, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSysroot, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSysrootUpgrader, g_object_unref)
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (OstreeRepoCommitTraverseIter, ostree_repo_commit_traverse_iter_clear)
+
+#endif
+
+G_END_DECLS
index 16951629eb855152ed2df05dc0002ce177d97fc1..05cb173eec2d8c2ed0b8ac90487b2fdb8ec5ea6b 100644 (file)
@@ -138,7 +138,8 @@ _ostree_bootloader_syslinux_write_config (OstreeBootloader          *bootloader,
                                                   bootversion);
 
   /* This should follow the symbolic link to the current bootversion. */
-  config_contents = gs_file_load_contents_utf8 (self->config_path, cancellable, error);
+  config_contents = glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (self->config_path), NULL,
+                                                    cancellable, error);
   if (!config_contents)
     goto out;
 
index f67e9bdb42fa75ee88d9172fe25a973db8b1e304..f95ea8431e92969124d858e6bdea257a90281b7a 100644 (file)
@@ -113,7 +113,8 @@ _ostree_bootloader_uboot_write_config (OstreeBootloader          *bootloader,
   g_autoptr(GPtrArray) new_lines = NULL;
 
   /* This should follow the symbolic link to the current bootversion. */
-  config_contents = gs_file_load_contents_utf8 (self->config_path, cancellable, error);
+  config_contents = glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (self->config_path), NULL,
+                                                    cancellable, error);
   if (!config_contents)
     return FALSE;
 
index a92aa4c7245332d8cbda5850360691cc7faf74fa..91d52f1bd309a0289fd5361a6a94a690166d1731 100644 (file)
@@ -83,7 +83,7 @@ _ostree_make_temporary_symlink_at (int             tmp_dirfd,
 
 GFileInfo * _ostree_header_gfile_info_new (mode_t mode, uid_t uid, gid_t gid);
 
-/* XX + / + checksum-2 + . + extension, but let's just use 256 for a
+/* XX/checksum-2.extension, but let's just use 256 for a
  * bit of overkill.
  */
 #define _OSTREE_LOOSE_PATH_MAX (256)
@@ -112,8 +112,7 @@ _ostree_get_relative_static_delta_part_path (const char        *from,
                                              const char        *to,
                                              guint              i);
 
-static inline char *
-_ostree_get_commitpartial_path (const char *checksum)
+static inline char * _ostree_get_commitpartial_path (const char *checksum)
 {
   return g_strconcat ("state/", checksum, ".commitpartial", NULL);
 }
index ded5e9769c21bd370d43e16b41010bcc886b0d35..8e8424fcb5dad36efa0a95a53bd1ef18fe326db9 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <gio/gfiledescriptorbased.h>
+#include "libglnx.h"
 #include "ostree.h"
 #include "ostree-core-private.h"
 #include "ostree-chain-input-stream.h"
 #include "otutil.h"
-#include "libglnx.h"
 
 #define ALIGN_VALUE(this, boundary) \
   (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
@@ -47,7 +47,7 @@ zlib_file_header_parse (GVariant         *metadata,
                         GError          **error);
 
 /**
- * SECTION:libostree-core
+ * SECTION:ostree-core
  * @title: Core repository-independent functions
  * @short_description: Create, validate, and convert core data types
  *
@@ -400,48 +400,40 @@ write_file_header_update_checksum (GOutputStream         *out,
   return ret;
 }
 
-/**
- * ostree_raw_file_to_content_stream:
+/*
+ * header_and_input_to_stream:
+ * @file_header: A file header
  * @input: File raw content stream
- * @file_info: A file info
- * @xattrs: (allow-none): Optional extended attributes
  * @out_input: (out): Serialized object stream
- * @out_length: (out): Length of stream
+ * @out_header_size: (out): Length of the header
  * @cancellable: Cancellable
  * @error: Error
  *
- * Convert from a "bare" file representation into an
- * OSTREE_OBJECT_TYPE_FILE stream.  This is a fundamental operation
- * for writing data to an #OstreeRepo.
+ * Combines @file_header and @input into a single stream.
  */
-gboolean
-ostree_raw_file_to_content_stream (GInputStream       *input,
-                                   GFileInfo          *file_info,
-                                   GVariant           *xattrs,
-                                   GInputStream      **out_input,
-                                   guint64            *out_length,
-                                   GCancellable       *cancellable,
-                                   GError            **error)
+static gboolean
+header_and_input_to_stream (GVariant           *file_header,
+                            GInputStream       *input,
+                            GInputStream      **out_input,
+                            guint64            *out_header_size,
+                            GCancellable       *cancellable,
+                            GError            **error)
 {
-  gboolean ret = FALSE;
   gpointer header_data;
   gsize header_size;
   g_autoptr(GInputStream) ret_input = NULL;
-  g_autoptr(GVariant) file_header = NULL;
   g_autoptr(GPtrArray) streams = NULL;
   g_autoptr(GOutputStream) header_out_stream = NULL;
   g_autoptr(GInputStream) header_in_stream = NULL;
 
-  file_header = file_header_new (file_info, xattrs);
-
   header_out_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
 
   if (!_ostree_write_variant_with_size (header_out_stream, file_header, 0, NULL, NULL,
                                         cancellable, error))
-    goto out;
+    return FALSE;
 
   if (!g_output_stream_close (header_out_stream, cancellable, error))
-    goto out;
+    return FALSE;
 
   header_size = g_memory_output_stream_get_data_size ((GMemoryOutputStream*) header_out_stream);
   header_data = g_memory_output_stream_steal_data ((GMemoryOutputStream*) header_out_stream);
@@ -452,15 +444,91 @@ ostree_raw_file_to_content_stream (GInputStream       *input,
   g_ptr_array_add (streams, g_object_ref (header_in_stream));
   if (input)
     g_ptr_array_add (streams, g_object_ref (input));
-  
-  ret_input = (GInputStream*)ostree_chain_input_stream_new (streams);
 
-  ret = TRUE;
+  ret_input = (GInputStream*)ostree_chain_input_stream_new (streams);
   ot_transfer_out_value (out_input, &ret_input);
+  if (out_header_size)
+    *out_header_size = header_size;
+
+  return TRUE;
+}
+
+/**
+ * ostree_raw_file_to_archive_z2_stream:
+ * @input: File raw content stream
+ * @file_info: A file info
+ * @xattrs: (allow-none): Optional extended attributes
+ * @out_input: (out): Serialized object stream
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Convert from a "bare" file representation into an
+ * OSTREE_OBJECT_TYPE_FILE stream suitable for ostree pull.
+ */
+gboolean
+ostree_raw_file_to_archive_z2_stream (GInputStream       *input,
+                                      GFileInfo          *file_info,
+                                      GVariant           *xattrs,
+                                      GInputStream      **out_input,
+                                      GCancellable       *cancellable,
+                                      GError            **error)
+{
+  g_autoptr(GVariant) file_header = NULL;
+  g_autoptr(GInputStream) zlib_input = NULL;
+
+  file_header = _ostree_zlib_file_header_new (file_info, xattrs);
+  if (input != NULL)
+    {
+      g_autoptr(GConverter) zlib_compressor = NULL;
+
+      zlib_compressor = G_CONVERTER (g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_RAW, 9));
+      zlib_input = g_converter_input_stream_new (input, zlib_compressor);
+    }
+  return header_and_input_to_stream (file_header,
+                                     zlib_input,
+                                     out_input,
+                                     NULL,
+                                     cancellable,
+                                     error);
+}
+
+/**
+ * ostree_raw_file_to_content_stream:
+ * @input: File raw content stream
+ * @file_info: A file info
+ * @xattrs: (allow-none): Optional extended attributes
+ * @out_input: (out): Serialized object stream
+ * @out_length: (out): Length of stream
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Convert from a "bare" file representation into an
+ * OSTREE_OBJECT_TYPE_FILE stream.  This is a fundamental operation
+ * for writing data to an #OstreeRepo.
+ */
+gboolean
+ostree_raw_file_to_content_stream (GInputStream       *input,
+                                   GFileInfo          *file_info,
+                                   GVariant           *xattrs,
+                                   GInputStream      **out_input,
+                                   guint64            *out_length,
+                                   GCancellable       *cancellable,
+                                   GError            **error)
+{
+  g_autoptr(GVariant) file_header = NULL;
+  guint64 header_size;
+
+  file_header = file_header_new (file_info, xattrs);
+  if (!header_and_input_to_stream (file_header,
+                                   input,
+                                   out_input,
+                                   &header_size,
+                                   cancellable,
+                                   error))
+    return FALSE;
   if (out_length)
     *out_length = header_size + g_file_info_get_size (file_info);
- out:
-  return ret;
+  return TRUE;
 }
 
 /**
@@ -613,7 +681,7 @@ ostree_content_file_parse_at (gboolean                compressed,
                               cancellable, error))
     goto out;
       
-  if (!gs_stream_fstat ((GFileDescriptorBased*)file_input, &stbuf, cancellable, error))
+  if (!glnx_stream_fstat ((GFileDescriptorBased*)file_input, &stbuf, error))
     goto out;
   
   if (!ostree_content_stream_parse (compressed, file_input, stbuf.st_size, trusted,
@@ -921,14 +989,13 @@ _ostree_make_temporary_symlink_at (int             tmp_dirfd,
                                    GError        **error)
 {
   gboolean ret = FALSE;
-  g_autofree char *tmpname = NULL;
+  char *tmpname = g_strdup ("tmplink.XXXXXX");
   guint i;
   const int max_attempts = 128;
 
   for (i = 0; i < max_attempts; i++)
     {
-      g_free (tmpname);
-      tmpname = gs_fileutil_gen_tmp_name (NULL, NULL);
+      glnx_gen_temp_name (tmpname);
       if (symlinkat (target, tmp_dirfd, tmpname) < 0)
         {
           if (errno == EEXIST)
index 29ef7b284712c9bc09b525460b0de5e616dd57e7..4a0e60e9ac90b7988685d7eed0f402594b8fe662 100644 (file)
@@ -59,7 +59,7 @@ G_BEGIN_DECLS
  * @OSTREE_OBJECT_TYPE_DIR_TREE: List of children (trees or files), and metadata
  * @OSTREE_OBJECT_TYPE_DIR_META: Directory metadata
  * @OSTREE_OBJECT_TYPE_COMMIT: Toplevel object, refers to tree and dirmeta for root
- * @OSTREE_OBJECT_TYPE_COMMIT_TOMBSTONE: Toplevel object, refers to a deleted commit
+ * @OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT: Toplevel object, refers to a deleted commit
  *
  * Enumeration for core object types; %OSTREE_OBJECT_TYPE_FILE is for
  * content, the other types are metadata.
@@ -90,10 +90,10 @@ typedef enum {
 /**
  * OSTREE_DIRMETA_GVARIANT_FORMAT:
  *
- * u - uid
- * u - gid
- * u - mode
- * a(ayay) - xattrs
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
  */
 #define OSTREE_DIRMETA_GVARIANT_STRING "(uuua(ayay))"
 #define OSTREE_DIRMETA_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_DIRMETA_GVARIANT_STRING)
@@ -106,10 +106,10 @@ typedef enum {
  * can't store in the real filesystem but we can still use a regular .file object
  * that we can hardlink to in the case of a user-mode checkout.
  *
- * u - uid
- * u - gid
- * u - mode
- * a(ayay) - xattrs
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
  */
 #define OSTREE_FILEMETA_GVARIANT_STRING "(uuua(ayay))"
 #define OSTREE_FILEMETA_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_FILEMETA_GVARIANT_STRING)
@@ -117,8 +117,8 @@ typedef enum {
 /**
  * OSTREE_TREE_GVARIANT_FORMAT:
  *
- * a(say) - array of (filename, checksum) for files
- * a(sayay) - array of (dirname, tree_checksum, meta_checksum) for directories
+ * a(say) - array of (filename, checksum) for files
+ * a(sayay) - array of (dirname, tree_checksum, meta_checksum) for directories
  */
 #define OSTREE_TREE_GVARIANT_STRING "(a(say)a(sayay))"
 #define OSTREE_TREE_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_TREE_GVARIANT_STRING)
@@ -126,14 +126,14 @@ typedef enum {
 /**
  * OSTREE_COMMIT_GVARIANT_FORMAT:
  *
- * a{sv} - Metadata
- * ay - parent checksum (empty string for initial)
- * a(say) - Related objects
- * s - subject 
- * s - body
- * t - Timestamp in seconds since the epoch (UTC)
- * ay - Root tree contents
- * ay - Root tree metadata
+ * a{sv} - Metadata
+ * ay - parent checksum (empty string for initial)
+ * a(say) - Related objects
+ * - s - subject
+ * s - body
+ * t - Timestamp in seconds since the epoch (UTC)
+ * ay - Root tree contents
+ * ay - Root tree metadata
  */
 #define OSTREE_COMMIT_GVARIANT_STRING "(a{sv}aya(say)sstayay)"
 #define OSTREE_COMMIT_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_COMMIT_GVARIANT_STRING)
@@ -141,8 +141,9 @@ typedef enum {
 /**
  * OSTREE_SUMMARY_GVARIANT_FORMAT:
  *
- * refs: a(s(taya{sv})) - Map of ref name -> (latest commit size, latest commit checksum, additional metadata), sorted by ref name
- * extensions: a{sv} - Additional metadata, none defined at the current time
+ * - a(s(taya{sv})) - Map of ref name -> (latest commit size, latest commit checksum, additional metadata), sorted by ref name
+ * - a{sv} - Additional metadata, at the current time the following are defined:
+ *   - key: "ostree.static-deltas", value: a{sv}, static delta name -> 32 bytes of checksum
  */
 #define OSTREE_SUMMARY_GVARIANT_STRING "(a(s(taya{sv}))a{sv})"
 #define OSTREE_SUMMARY_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_SUMMARY_GVARIANT_STRING)
@@ -152,9 +153,9 @@ typedef enum {
 
 /**
  * OstreeRepoMode:
- * @OSTREE_REPO_MODE_BARE: Files are stored as themselves; can only be written as root
+ * @OSTREE_REPO_MODE_BARE: Files are stored as themselves; checkouts are hardlinks; can only be written as root
  * @OSTREE_REPO_MODE_ARCHIVE_Z2: Files are compressed, should be owned by non-root.  Can be served via HTTP
- * @OSTREE_REPO_MODE_BARE_USER: Files are stored as themselves, except ownership; can be written by user
+ * @OSTREE_REPO_MODE_BARE_USER: Files are stored as themselves, except ownership; can be written by user. Hardlinks work only in user checkouts.
  *
  * See the documentation of #OstreeRepo for more information about the
  * possible modes.
@@ -165,8 +166,8 @@ typedef enum {
   OSTREE_REPO_MODE_BARE_USER
 } OstreeRepoMode;
 
-const _OSTREE_PUBLIC
-GVariantType *ostree_metadata_variant_type (OstreeObjectType objtype);
+_OSTREE_PUBLIC
+const GVariantType *ostree_metadata_variant_type (OstreeObjectType objtype);
 
 _OSTREE_PUBLIC
 gboolean ostree_validate_checksum_string (const char *sha256,
@@ -214,9 +215,6 @@ gboolean ostree_parse_refspec (const char *refspec,
                                char      **out_ref,
                                GError    **error);
 
-_OSTREE_PUBLIC
-void ostree_checksum_update_meta (GChecksum *checksum, GFileInfo *file_info, GVariant  *xattrs);
-
 _OSTREE_PUBLIC
 const char * ostree_object_type_to_string (OstreeObjectType objtype);
 
@@ -244,7 +242,8 @@ void ostree_object_from_string (const char *str,
                                 gchar     **out_checksum,
                                 OstreeObjectType *out_objtype);
 
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
 ostree_content_stream_parse (gboolean                compressed,
                              GInputStream           *input,
                              guint64                 input_length,
@@ -276,6 +275,15 @@ gboolean ostree_content_file_parse_at (gboolean                compressed,
                                        GCancellable           *cancellable,
                                        GError                **error);
 
+_OSTREE_PUBLIC
+gboolean
+ostree_raw_file_to_archive_z2_stream (GInputStream       *input,
+                                      GFileInfo          *file_info,
+                                      GVariant           *xattrs,
+                                      GInputStream      **out_input,
+                                      GCancellable       *cancellable,
+                                      GError            **error);
+
 _OSTREE_PUBLIC
 gboolean ostree_raw_file_to_content_stream (GInputStream       *input,
                                             GFileInfo          *file_info,
index 856a3987b070f609e198759a60a24c88e4f97653..01fdf1bf72a1b05ab5dee8e07e3a9bbb60491c4a 100644 (file)
 
 G_BEGIN_DECLS
 
+
+/**
+ * OstreeDeployment:
+ * @parent_instance:
+ * @index: Global offset
+ * @osname:
+ * @csum: OSTree checksum of tree
+ * @deployserial: How many times this particular csum appears in deployment list
+ * @bootcsum: Checksum of kernel+initramfs
+ * @bootserial: An integer assigned to this tree per its ${bootcsum}
+ * @bootconfig: Bootloader configuration
+ * @origin: How to construct an upgraded version of this tree
+ * @unlocked: The unlocked state
+ */
 struct _OstreeDeployment
 {
   GObject       parent_instance;
 
-  int index;  /* Global offset */
-  char *osname;  /* osname */
-  char *csum;  /* OSTree checksum of tree */
-  int deployserial;  /* How many times this particular csum appears in deployment list */
-  char *bootcsum;  /* Checksum of kernel+initramfs */
-  int bootserial; /* An integer assigned to this tree per its ${bootcsum} */
-  OstreeBootconfigParser *bootconfig; /* Bootloader configuration */
-  GKeyFile *origin; /* How to construct an upgraded version of this tree */
-  OstreeDeploymentUnlockedState unlocked;  /* The unlocked state */
+  int index;
+  char *osname;
+  char *csum;
+  int deployserial;
+  char *bootcsum;
+  int bootserial;
+  OstreeBootconfigParser *bootconfig;
+  GKeyFile *origin;
+  OstreeDeploymentUnlockedState unlocked;
 };
 
 void _ostree_deployment_set_bootcsum (OstreeDeployment *self, const char *bootcsum);
index 4b733461d00fe4b2c4441b786cddc4022a4c0fdf..69a67b7600c65ef42eff2ed909fa2d608e644c6d 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "config.h"
 
+#include "libglnx.h"
 #include "ostree.h"
 #include "otutil.h"
 
@@ -209,6 +210,8 @@ diff_add_dir_recurse (GFile          *d,
  * @modified: (element-type OstreeDiffItem): Modified files
  * @removed: (element-type Gio.File): Removed files
  * @added: (element-type Gio.File): Added files
+ * @cancellable: Cancellable
+ * @error: Error
  *
  * Compute the difference between directory @a and @b as 3 separate
  * sets of #OstreeDiffItem in @modified, @removed, and @added.
index f4db23ef47bce4501b3a494f527ad7f5e9ede31f..781bf7681dc2bd63d70a85a850a8d5c6a6ae8a58 100644 (file)
 
 G_BEGIN_DECLS
 
+/**
+ * OstreeDiffFlags:
+ */
 typedef enum {
   OSTREE_DIFF_FLAGS_NONE = 0,
   OSTREE_DIFF_FLAGS_IGNORE_XATTRS = (1 << 0)
 } OstreeDiffFlags;
 
+/**
+ * OstreeDiffItem:
+ */
 typedef struct _OstreeDiffItem OstreeDiffItem;
 struct _OstreeDiffItem
 {
diff --git a/src/libostree/ostree-dummy-enumtypes.c b/src/libostree/ostree-dummy-enumtypes.c
new file mode 100644 (file)
index 0000000..259273b
--- /dev/null
@@ -0,0 +1,31 @@
+/* This file declares a stub function that is only exported
+ * to pacify ABI checkers - no one could really have used it.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "ostree-dummy-enumtypes.h"
+
+/* Exported for backwards compat - see 
+ * https://bugzilla.gnome.org/show_bug.cgi?id=764131
+ */
+GType
+ostree_fetcher_config_flags_get_type (void)
+{
+  return G_TYPE_INVALID;
+}
diff --git a/src/libostree/ostree-dummy-enumtypes.h b/src/libostree/ostree-dummy-enumtypes.h
new file mode 100644 (file)
index 0000000..017ed28
--- /dev/null
@@ -0,0 +1,29 @@
+/* This file declares a stub function that is only exported
+ * to pacify ABI checkers - no one could really have used it.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#ifndef __GI_SCANNER__
+_OSTREE_PUBLIC GType
+ostree_fetcher_config_flags_get_type (void);
+#endif
index fe8807ae7d518817e28e2df5a1934d07ae0be058..ab1b2aec7586a0846aba8ef23d88670af04f9092 100644 (file)
@@ -28,7 +28,7 @@
 
 /*** BEGIN value-header ***/
 GType
-@enum_name@_get_type (void)
+_@enum_name@_get_type (void)
 {
   static volatile gsize the_type__volatile = 0;
 
index 40899d7fac28f39d55b53595e94bfa093ec083ce..ec20fe0d18a553f0b80724aeff9f6ed2f6348b52 100644 (file)
@@ -32,9 +32,8 @@ G_BEGIN_DECLS
 /*** END file-production ***/
 
 /*** BEGIN enumeration-production ***/
-#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
-_OSTREE_PUBLIC
-GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (_@enum_name@_get_type ())
+GType _@enum_name@_get_type (void) G_GNUC_CONST;
 
 /*** END enumeration-production ***/
 
index d7915ba69fdfaf5ae47f2ddf93a8d15fb4a9cf27..313df6a8dc8bdcc0c100a05d4eebf482bb227c4c 100644 (file)
@@ -25,6 +25,7 @@
 #include <gio/gfiledescriptorbased.h>
 #include <gio/gunixoutputstream.h>
 
+#include "libglnx.h"
 #include "ostree-fetcher.h"
 #ifdef HAVE_LIBSOUP_CLIENT_CERTS
 #include "ostree-tls-cert-interaction.h"
@@ -66,7 +67,12 @@ typedef struct {
   guint64 total_downloaded;
 } ThreadClosure;
 
+static void
+session_thread_process_pending_queue (ThreadClosure *thread_closure);
+
 typedef struct {
+  volatile int ref_count;
+
   ThreadClosure *thread_closure;
   SoupURI *uri;
 
@@ -185,10 +191,22 @@ pending_task_compare (gconstpointer a,
          (priority_a < priority_b) ? -1 : 1;
 }
 
+static OstreeFetcherPendingURI *
+pending_uri_ref (OstreeFetcherPendingURI *pending)
+{
+  g_return_val_if_fail (pending != NULL, NULL);
+  g_return_val_if_fail (pending->ref_count > 0, NULL);
+
+  g_atomic_int_inc (&pending->ref_count);
+
+  return pending;
+}
+
 static void
-pending_uri_free (OstreeFetcherPendingURI *pending)
+pending_uri_unref (OstreeFetcherPendingURI *pending)
 {
-  g_hash_table_remove (pending->thread_closure->outstanding, pending);
+  if (!g_atomic_int_dec_and_test (&pending->ref_count))
+    return;
 
   g_clear_pointer (&pending->thread_closure, thread_closure_unref);
 
@@ -273,6 +291,7 @@ session_thread_set_proxy_cb (ThreadClosure *thread_closure,
                 proxy_uri, NULL);
 }
 
+#ifdef HAVE_LIBSOUP_CLIENT_CERTS
 static void
 session_thread_set_tls_interaction_cb (ThreadClosure *thread_closure,
                                        gpointer data)
@@ -288,6 +307,7 @@ session_thread_set_tls_interaction_cb (ThreadClosure *thread_closure,
                 SOUP_SESSION_TLS_INTERACTION,
                 interaction, NULL);
 }
+#endif
 
 static void
 session_thread_set_tls_database_cb (ThreadClosure *thread_closure,
@@ -328,8 +348,7 @@ session_thread_process_pending_queue (ThreadClosure *thread_closure)
       pending = g_task_get_task_data (task);
       cancellable = g_task_get_cancellable (task);
 
-      /* pending_uri_free() removes this. */
-      g_hash_table_add (thread_closure->outstanding, pending);
+      g_hash_table_add (thread_closure->outstanding, pending_uri_ref (pending));
 
       soup_request_send_async (pending->request,
                                cancellable,
@@ -383,7 +402,7 @@ session_thread_request_uri (ThreadClosure *thread_closure,
       if (thread_closure->tmpdir_name == NULL)
         {
           if (!_ostree_repo_allocate_tmpdir (thread_closure->base_tmpdir_dfd,
-                                             "fetcher-",
+                                             OSTREE_REPO_TMPDIR_FETCHER,
                                              &thread_closure->tmpdir_name,
                                              &thread_closure->tmpdir_dfd,
                                              &thread_closure->tmpdir_lock,
@@ -537,7 +556,7 @@ _ostree_fetcher_constructed (GObject *object)
   self->thread_closure->tmpdir_dfd = -1;
   self->thread_closure->tmpdir_lock = empty_lockfile;
 
-  self->thread_closure->outstanding = g_hash_table_new (NULL, NULL);
+  self->thread_closure->outstanding = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)pending_uri_unref);
   self->thread_closure->output_stream_set = g_hash_table_new_full (NULL, NULL,
                                                                    (GDestroyNotify) NULL,
                                                                    (GDestroyNotify) g_object_unref);
@@ -739,6 +758,18 @@ on_stream_read (GObject        *object,
                 GAsyncResult   *result,
                 gpointer        user_data);
 
+static void
+remove_pending_rerun_queue (OstreeFetcherPendingURI *pending)
+{
+  /* Hold a temporary ref to ensure the reference to
+   * pending->thread_closure is valid.
+   */
+  pending_uri_ref (pending);
+  g_hash_table_remove (pending->thread_closure->outstanding, pending);
+  session_thread_process_pending_queue (pending->thread_closure);
+  pending_uri_unref (pending);
+}
+
 static void
 on_out_splice_complete (GObject        *object,
                         GAsyncResult   *result,
@@ -767,7 +798,10 @@ on_out_splice_complete (GObject        *object,
 
  out:
   if (local_error)
-    g_task_return_error (task, local_error);
+    {
+      g_task_return_error (task, local_error);
+      remove_pending_rerun_queue (pending);
+    }
 
   g_object_unref (task);
 }
@@ -799,6 +833,7 @@ on_stream_read (GObject        *object,
       g_task_return_pointer (task,
                              g_strdup (pending->out_tmpfile),
                              (GDestroyNotify) g_free);
+      remove_pending_rerun_queue (pending);
     }
   else
     {
@@ -834,7 +869,10 @@ on_stream_read (GObject        *object,
 
  out:
   if (local_error)
-    g_task_return_error (task, local_error);
+    {
+      g_task_return_error (task, local_error);
+      remove_pending_rerun_queue (pending);
+    }
 
   g_object_unref (task);
 }
@@ -880,6 +918,7 @@ on_request_sent (GObject        *object,
                                      g_strdup (pending->out_tmpfile),
                                      (GDestroyNotify) g_free);
             }
+          remove_pending_rerun_queue (pending);
           goto out;
         }
       else if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
@@ -944,6 +983,7 @@ on_request_sent (GObject        *object,
       g_task_return_pointer (task,
                              g_object_ref (pending->request_body),
                              (GDestroyNotify) g_object_unref);
+      remove_pending_rerun_queue (pending);
     }
   
  out:
@@ -952,6 +992,7 @@ on_request_sent (GObject        *object,
       if (pending->request_body)
         (void) g_input_stream_close (pending->request_body, NULL, NULL);
       g_task_return_error (task, local_error);
+      remove_pending_rerun_queue (pending);
     }
 
   g_object_unref (task);
@@ -976,6 +1017,7 @@ ostree_fetcher_request_uri_internal (OstreeFetcher         *self,
 
   /* SoupRequest is created in session thread. */
   pending = g_new0 (OstreeFetcherPendingURI, 1);
+  pending->ref_count = 1;
   pending->thread_closure = thread_closure_ref (self->thread_closure);
   pending->uri = soup_uri_copy (uri);
   pending->max_size = max_size;
@@ -983,7 +1025,7 @@ ostree_fetcher_request_uri_internal (OstreeFetcher         *self,
 
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, source_tag);
-  g_task_set_task_data (task, pending, (GDestroyNotify) pending_uri_free);
+  g_task_set_task_data (task, pending, (GDestroyNotify) pending_uri_unref);
 
   /* We'll use the GTask priority for our own priority queue. */
   g_task_set_priority (task, priority);
@@ -1067,7 +1109,7 @@ _ostree_fetcher_bytes_transferred (OstreeFetcher       *self)
       
       if (G_IS_FILE_DESCRIPTOR_BASED (stream))
         {
-          if (gs_stream_fstat ((GFileDescriptorBased*)stream, &stbuf, NULL, NULL))
+          if (glnx_stream_fstat ((GFileDescriptorBased*)stream, &stbuf, NULL))
             ret += stbuf.st_size;
         }
     }
index 209f73423a97684176bf3816694fdf964492ffe2..2db39f3b5e588b488480f121302fffecf2d3b0b3 100644 (file)
@@ -35,6 +35,11 @@ G_BEGIN_DECLS
 
 typedef struct OstreeGpgVerifier OstreeGpgVerifier;
 
+/* If this type becomes public in future, move this autoptr cleanup
+ * definition to the ostree-autocleanups.h header file. Right now it
+ * relies on glnx's fallback definition of the macro. */
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeGpgVerifier, g_object_unref)
+
 GType _ostree_gpg_verifier_get_type (void);
 
 OstreeGpgVerifier *_ostree_gpg_verifier_new (void);
index 37fbfb5cd93aa0ec70aa623ea4817721f1f7d7dc..fa4614d1e352042b7532a3b40c3fdbf347cb11e1 100644 (file)
@@ -27,7 +27,7 @@
 #include "ostree-gpg-verify-result-private.h"
 
 /**
- * SECTION: libostree-gpg-verify-result
+ * SECTION: ostree-gpg-verify-result
  * @title: GPG signature verification results
  * @short_description: Inspect detached GPG signatures
  *
@@ -622,3 +622,33 @@ ostree_gpg_verify_result_describe_variant (GVariant *variant,
         }
     }
 }
+
+/**
+ * ostree_gpg_verify_result_require_valid_signature:
+ * @result: (nullable): an #OstreeGpgVerifyResult
+ * @error: A #GError
+ *
+ * Checks if the result contains at least one signature from the
+ * trusted keyring.  You can call this function immediately after
+ * ostree_repo_verify_summary() or ostree_repo_verify_commit_ext() -
+ * it will handle the %NULL @result and filled @error too.
+ *
+ * Returns: %TRUE if @result was not %NULL and had at least one
+ * signature from trusted keyring, otherwise %FALSE
+ */
+gboolean
+ostree_gpg_verify_result_require_valid_signature (OstreeGpgVerifyResult *result,
+                                                  GError **error)
+{
+  if (result == NULL)
+    return FALSE;
+
+  if (ostree_gpg_verify_result_count_valid (result) == 0)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "GPG signatures found, but none are in trusted keyring");
+      return FALSE;
+    }
+
+  return TRUE;
+}
index 8894afdf1784ca46c47879817750dbbdec0f5b33..f95125384fb88e247a688a06775a7e82c01d2cc2 100644 (file)
@@ -133,4 +133,8 @@ void ostree_gpg_verify_result_describe_variant (GVariant *variant,
                                                 const gchar *line_prefix,
                                                 OstreeGpgSignatureFormatFlags flags);
 
+_OSTREE_PUBLIC
+gboolean ostree_gpg_verify_result_require_valid_signature (OstreeGpgVerifyResult *result,
+                                                           GError **error);
+
 G_END_DECLS
index 4151fa34361b7cf2dc4e023bc6f69c4f625cd0b1..b270a94ecf234337a154e39601d2d979c2e714d0 100644 (file)
@@ -107,7 +107,7 @@ _ostree_linuxfs_alter_immutable_flag (GFile         *path,
                                       GError       **error)
 {
   gboolean ret = FALSE;
-  int fd = -1;
+  glnx_fd_close int fd = -1;
 
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return FALSE;
@@ -129,7 +129,5 @@ _ostree_linuxfs_alter_immutable_flag (GFile         *path,
 
   ret = TRUE;
  out:
-  if (fd != -1)
-    (void) close (fd);
   return ret;
 }
index 1ec03c41e03e7ec716265ce3f22ef3e66e6b2cbc..8d9dcbc91e490b839b7f7feb1dd34b0e7f0e2006 100644 (file)
@@ -32,7 +32,7 @@ enum {
 
 /**
  * SECTION:ostree-lzma-compressor
- * @short_description: LZMA compressor
+ * @title: LZMA compressor
  *
  * An implementation of #GConverter that compresses data using
  * LZMA.
index b46e8fb2c096ca2bc1361cc99e515db136c00c16..6376e3892713c006ca22aa13a84b630abbfe4a86 100644 (file)
@@ -29,6 +29,14 @@ enum {
   PROP_0,
 };
 
+/**
+ * SECTION:ostree-lzma-decompressor
+ * @title: LZMA decompressor
+ *
+ * An implementation of #GConverter that decompresses data using
+ * LZMA.
+ */
+
 static void _ostree_lzma_decompressor_iface_init          (GConverterIface *iface);
 
 struct _OstreeLzmaDecompressor
index bc4f4250f792fb87d6049dd6f5b060155b340732..5540cc7c74363cb3fb88878220c8ec4fc2597ec3 100644 (file)
@@ -27,7 +27,7 @@
 #include "ostree-core.h"
 
 /**
- * SECTION:libostree-mutable-tree
+ * SECTION:ostree-mutable-tree
  * @title: In-memory modifiable filesystem tree
  * @short_description: Modifiable filesystem tree
  *
@@ -159,6 +159,11 @@ ostree_mutable_tree_replace_file (OstreeMutableTree *self,
 {
   gboolean ret = FALSE;
 
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  if (!ot_util_filename_validate (name, error))
+    goto out;
+
   if (g_hash_table_lookup (self->subdirs, name))
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -187,6 +192,9 @@ ostree_mutable_tree_ensure_dir (OstreeMutableTree *self,
 
   g_return_val_if_fail (name != NULL, FALSE);
 
+  if (!ot_util_filename_validate (name, error))
+    goto out;
+
   if (g_hash_table_lookup (self->files, name))
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
index 30425d8df4b592b8a4ff6d009bbbb759c0ce2902..1b642c42644839caf579da749a75f64954f6e3eb 100644 (file)
@@ -84,7 +84,8 @@ gboolean ostree_mutable_tree_lookup (OstreeMutableTree   *self,
                                      OstreeMutableTree  **out_subdir,
                                      GError             **error);
 
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
 ostree_mutable_tree_ensure_parent_dirs (OstreeMutableTree  *self,
                                         GPtrArray          *split_path,
                                         const char         *metadata_checksum,
index 0a77da0280b9daeeb70c87750f407e260eb72cee..5f7cf0bded2fe8588cbac75ba777fc9f896ffad5 100644 (file)
@@ -632,7 +632,7 @@ checkout_tree_at (OstreeRepo                        *self,
 {
   gboolean ret = FALSE;
   gboolean did_exist = FALSE;
-  int destination_dfd = -1;
+  glnx_fd_close int destination_dfd = -1;
   int res;
   g_autoptr(GVariant) xattrs = NULL;
   g_autoptr(GFileEnumerator) dir_enum = NULL;
@@ -752,12 +752,12 @@ checkout_tree_at (OstreeRepo                        *self,
         }
     }
 
-  /* Set directory mtime to 0, so that it is constant for all checkouts.
+  /* Set directory mtime to OSTREE_TIMESTAMP, so that it is constant for all checkouts.
    * Must be done after setting permissions and creating all children.
    */
   if (!did_exist)
     {
-      const struct timespec times[2] = { { 0, UTIME_OMIT }, { 0, } };
+      const struct timespec times[2] = { { OSTREE_TIMESTAMP, UTIME_OMIT }, { OSTREE_TIMESTAMP, 0} };
       do
         res = futimens (destination_dfd, times);
       while (G_UNLIKELY (res == -1 && errno == EINTR));
@@ -779,8 +779,6 @@ checkout_tree_at (OstreeRepo                        *self,
 
   ret = TRUE;
  out:
-  if (destination_dfd != -1)
-    (void) close (destination_dfd);
   return ret;
 }
 
@@ -957,38 +955,36 @@ ostree_repo_checkout_gc (OstreeRepo        *self,
     g_hash_table_iter_init (&iter, to_clean_dirs);
   while (to_clean_dirs && g_hash_table_iter_next (&iter, &key, &value))
     {
-      g_autoptr(GFile) objdir = NULL;
-      g_autoptr(GFileEnumerator) enumerator = NULL;
-      g_autofree char *objdir_name = NULL;
-
-      objdir_name = g_strdup_printf ("%02x", GPOINTER_TO_UINT (key));
-      objdir = g_file_get_child (self->uncompressed_objects_dir, objdir_name);
-
-      enumerator = g_file_enumerate_children (objdir, "standard::name,standard::type,unix::inode,unix::nlink", 
-                                              G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                              cancellable, 
-                                              error);
-      if (!enumerator)
+      g_autofree char *objdir_name = g_strdup_printf ("%02x", GPOINTER_TO_UINT (key));
+      g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
+
+      if (!glnx_dirfd_iterator_init_at (self->uncompressed_objects_dir_fd, objdir_name, FALSE,
+                                        &dfd_iter, error))
         goto out;
-  
+
       while (TRUE)
         {
-          GFileInfo *file_info;
-          guint32 nlinks;
+          struct dirent *dent;
+          struct stat stbuf;
 
-          if (!gs_file_enumerator_iterate (enumerator, &file_info, NULL,
-                                           cancellable, error))
+          if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
             goto out;
-          if (file_info == NULL)
+          if (dent == NULL)
             break;
+
+          if (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
+            {
+              glnx_set_error_from_errno (error);
+              goto out;
+            }
           
-          nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
-          if (nlinks == 1)
+          if (stbuf.st_nlink == 1)
             {
-              g_autoptr(GFile) objpath = NULL;
-              objpath = g_file_get_child (objdir, g_file_info_get_name (file_info));
-              if (!gs_file_unlink (objpath, cancellable, error))
-                goto out;
+              if (unlinkat (dfd_iter.fd, dent->d_name, 0) != 0)
+                {
+                  glnx_set_error_from_errno (error);
+                  goto out;
+                }
             }
         }
     }
index 7f03e11d21b040da8dff959a0da88e6ba04793af..685eadd0f9de1b7706134b5e7774f67bc762b12f 100644 (file)
 #include <sys/xattr.h>
 #include <glib/gprintf.h>
 
-struct OstreeRepoCommitModifier {
-  volatile gint refcount;
-
-  OstreeRepoCommitModifierFlags flags;
-  OstreeRepoCommitFilter filter;
-  gpointer user_data;
-  GDestroyNotify destroy_notify;
-
-  OstreeRepoCommitModifierXattrCallback xattr_callback;
-  GDestroyNotify xattr_destroy;
-  gpointer xattr_user_data;
-
-  OstreeSePolicy *sepolicy;
-  GHashTable *devino_cache;
-};
-
 gboolean
 _ostree_repo_ensure_loose_objdir_at (int             dfd,
                                      const char     *loose_path,
@@ -226,7 +210,6 @@ commit_loose_object_trusted (OstreeRepo        *self,
   else
     {
       int res;
-      struct timespec times[2];
 
       if (objtype == OSTREE_OBJECT_TYPE_FILE && self->mode == OSTREE_REPO_MODE_BARE)
         {
@@ -282,12 +265,9 @@ commit_loose_object_trusted (OstreeRepo        *self,
         {
           /* To satisfy tools such as guile which compare mtimes
            * to determine whether or not source files need to be compiled,
-           * set the modification time to 0.
+           * set the modification time to OSTREE_TIMESTAMP.
            */
-          times[0].tv_sec = 0; /* atime */
-          times[0].tv_nsec = UTIME_OMIT;
-          times[1].tv_sec = 0; /* mtime */
-          times[1].tv_nsec = 0;
+          const struct timespec times[2] = { { OSTREE_TIMESTAMP, UTIME_OMIT }, { OSTREE_TIMESTAMP, 0} };
           do
             res = futimens (fd, times);
           while (G_UNLIKELY (res == -1 && errno == EINTR));
@@ -573,11 +553,8 @@ _ostree_repo_open_trusted_content_bare (OstreeRepo          *self,
   g_autofree char *temp_filename = NULL;
   g_autoptr(GOutputStream) ret_stream = NULL;
   gboolean have_obj;
-  char loose_objpath[_OSTREE_LOOSE_PATH_MAX];
 
-  if (!_ostree_repo_has_loose_object (self, checksum, OSTREE_OBJECT_TYPE_FILE,
-                                      &have_obj, loose_objpath,
-                                      NULL,
+  if (!_ostree_repo_has_loose_object (self, checksum, OSTREE_OBJECT_TYPE_FILE, &have_obj,
                                       cancellable, error))
     goto out;
 
@@ -662,7 +639,6 @@ write_object (OstreeRepo         *self,
   gboolean temp_file_is_regular;
   gboolean temp_file_is_symlink;
   gboolean object_is_symlink = FALSE;
-  char loose_objpath[_OSTREE_LOOSE_PATH_MAX];
   gssize unpacked_size = 0;
   gboolean indexable = FALSE;
 
@@ -673,9 +649,8 @@ write_object (OstreeRepo         *self,
 
   if (expected_checksum)
     {
-      if (!_ostree_repo_has_loose_object (self, expected_checksum, objtype,
-                                          &have_obj, loose_objpath,
-                                          NULL, cancellable, error))
+      if (!_ostree_repo_has_loose_object (self, expected_checksum, objtype, &have_obj,
+                                          cancellable, error))
         goto out;
       if (have_obj)
         {
@@ -852,8 +827,7 @@ write_object (OstreeRepo         *self,
       repo_store_size_entry (self, actual_checksum, unpacked_size, stbuf.st_size);
     }
 
-  if (!_ostree_repo_has_loose_object (self, actual_checksum, objtype,
-                                      &have_obj, loose_objpath, NULL,
+  if (!_ostree_repo_has_loose_object (self, actual_checksum, objtype, &have_obj,
                                       cancellable, error))
     goto out;
           
@@ -1167,7 +1141,6 @@ ostree_repo_prepare_transaction (OstreeRepo     *self,
 {
   gboolean ret = FALSE;
   gboolean ret_transaction_resume = FALSE;
-  g_autofree char *stagedir_boot_id_prefix = NULL;
   g_autofree char *stagedir_name = NULL;
   glnx_fd_close int stagedir_fd = -1;
   g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
@@ -1178,10 +1151,8 @@ ostree_repo_prepare_transaction (OstreeRepo     *self,
 
   self->in_transaction = TRUE;
 
-  stagedir_boot_id_prefix = g_strconcat ("staging-", self->boot_id, "-", NULL);
-
   if (!_ostree_repo_allocate_tmpdir (self->tmp_dir_fd,
-                                     stagedir_boot_id_prefix,
+                                     self->stagedir_prefix,
                                      &self->commit_stagedir_name,
                                      &self->commit_stagedir_fd,
                                      &self->commit_stagedir_lock,
@@ -1287,44 +1258,87 @@ cleanup_tmpdir (OstreeRepo        *self,
                 GError           **error)
 {
   gboolean ret = FALSE;
-  g_autoptr(GFileEnumerator) enumerator = NULL;
+  g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
   guint64 curtime_secs;
 
-  enumerator = g_file_enumerate_children (self->tmp_dir, "standard::name,time::modified",
-                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                          cancellable,
-                                          error);
-  if (!enumerator)
-    goto out;
-
   curtime_secs = g_get_real_time () / 1000000;
 
+  if (!glnx_dirfd_iterator_init_at (self->tmp_dir_fd, ".", TRUE, &dfd_iter, error))
+    goto out;
+
   while (TRUE)
     {
-      GFileInfo *file_info;
-      GFile *path;
-      guint64 mtime;
       guint64 delta;
+      struct dirent *dent;
+      struct stat stbuf;
+      g_auto(GLnxLockFile) lockfile = GLNX_LOCK_FILE_INIT;
+      gboolean did_lock;
 
-      if (!gs_file_enumerator_iterate (enumerator, &file_info, &path,
-                                       cancellable, error))
+      if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
         goto out;
-      if (file_info == NULL)
+
+      if (dent == NULL)
         break;
 
-      mtime = g_file_info_get_attribute_uint64 (file_info, "time::modified");
-      if (mtime > curtime_secs)
-        continue;
-      /* Only delete files older than a day.  To do better, we would
-       * need to coordinate between multiple processes in a reliable
-       * fashion.  See
-       * https://bugzilla.gnome.org/show_bug.cgi?id=709115
+      if (TEMP_FAILURE_RETRY (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW)) < 0)
+        {
+          if (errno == ENOENT) /* Did another cleanup win? */
+            continue;
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+
+      /* First, if it's a directory which needs locking, but it's
+       * busy, skip it.
        */
-      delta = curtime_secs - mtime;
-      if (delta > 60*60*24)
+      if (_ostree_repo_is_locked_tmpdir (dent->d_name))
         {
-          if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (path), cancellable, error))
+          if (!_ostree_repo_try_lock_tmpdir (dfd_iter.fd, dent->d_name,
+                                             &lockfile, &did_lock, error))
             goto out;
+          if (!did_lock)
+            continue;
+        }
+
+      /* If however this is the staging directory for the *current*
+       * boot, then don't delete it now - we may end up reusing it, as
+       * is the point.
+       */
+      if (g_str_has_prefix (dent->d_name, self->stagedir_prefix))
+        continue;
+      else if (g_str_has_prefix (dent->d_name, OSTREE_REPO_TMPDIR_STAGING))
+        {
+          /* But, crucially we can now clean up staging directories
+           * from *other* boots
+           */
+          if (!glnx_shutil_rm_rf_at (dfd_iter.fd, dent->d_name, cancellable, error))
+            goto out;
+        }
+      /* FIXME - move OSTREE_REPO_TMPDIR_FETCHER underneath the
+       * staging/boot-id scheme as well, since all of the "did it get
+       * fsync'd" concerns apply to that as well.  Then we can skip
+       * this special case.
+       */
+      else if (g_str_has_prefix (dent->d_name, OSTREE_REPO_TMPDIR_FETCHER))
+        continue;
+      else
+        {
+          /* Now we do time-based cleanup.  Ignore it if it's somehow
+           * in the future...
+           */
+          if (stbuf.st_mtime > curtime_secs)
+            continue;
+
+          /* Now, we're pruning content based on the expiry, which
+           * defaults to a day.  That's what we were doing before we
+           * had locking...but in future we can be smarter here.
+           */
+          delta = curtime_secs - stbuf.st_mtime;
+          if (delta > self->tmp_expiry_seconds)
+            {
+              if (!glnx_shutil_rm_rf_at (dfd_iter.fd, dent->d_name, cancellable, error))
+                goto out;
+            }
         }
     }
 
@@ -1448,12 +1462,25 @@ ostree_repo_commit_transaction (OstreeRepo                  *self,
 
   g_return_val_if_fail (self->in_transaction == TRUE, FALSE);
 
-  if (syncfs (self->tmp_dir_fd) < 0)
+  if ((self->test_error_flags & OSTREE_REPO_TEST_ERROR_PRE_COMMIT) > 0)
     {
-      glnx_set_error_from_errno (error);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "OSTREE_REPO_TEST_ERROR_PRE_COMMIT specified");
       goto out;
     }
 
+  /* FIXME: Added since valgrind in el7 doesn't know about
+   * `syncfs`...we should delete this later.
+   */
+  if (g_getenv ("OSTREE_SUPPRESS_SYNCFS") == NULL)
+    {
+      if (syncfs (self->tmp_dir_fd) < 0)
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+    }
+
   if (!rename_pending_loose_objects (self, cancellable, error))
     goto out;
 
@@ -1937,7 +1964,7 @@ create_empty_gvariant_dict (void)
  * ostree_repo_write_commit:
  * @self: Repo
  * @parent: (allow-none): ASCII SHA256 checksum for parent, or %NULL for none
- * @subject: Subject
+ * @subject: (allow-none): Subject
  * @body: (allow-none): Body
  * @metadata: (allow-none): GVariant of type a{sv}, or %NULL for none
  * @root: The tree to point the commit to
@@ -1981,10 +2008,11 @@ ostree_repo_write_commit (OstreeRepo      *self,
  * ostree_repo_write_commit_with_time:
  * @self: Repo
  * @parent: (allow-none): ASCII SHA256 checksum for parent, or %NULL for none
- * @subject: Subject
+ * @subject: (allow-none): Subject
  * @body: (allow-none): Body
  * @metadata: (allow-none): GVariant of type a{sv}, or %NULL for none
  * @root: The tree to point the commit to
+ * @time: The time to use to stamp the commit
  * @out_commit: (out): Resulting ASCII SHA256 checksum for commit
  * @cancellable: Cancellable
  * @error: Error
@@ -2011,8 +2039,6 @@ ostree_repo_write_commit_with_time (OstreeRepo      *self,
   g_autofree guchar *commit_csum = NULL;
   OstreeRepoFile *repo_root = OSTREE_REPO_FILE (root);
 
-  g_return_val_if_fail (subject != NULL, FALSE);
-
   /* Add sizes information to our metadata object */
   if (!add_size_index_to_metadata (self, metadata, &new_metadata,
                                    cancellable, error))
@@ -2022,7 +2048,7 @@ ostree_repo_write_commit_with_time (OstreeRepo      *self,
                           new_metadata ? new_metadata : create_empty_gvariant_dict (),
                           parent ? ostree_checksum_to_bytes_v (parent) : ot_gvariant_new_bytearray (NULL, 0),
                           g_variant_new_array (G_VARIANT_TYPE ("(say)"), NULL, 0),
-                          subject, body ? body : "",
+                          subject ? subject : "", body ? body : "",
                           GUINT64_TO_BE (time),
                           ostree_checksum_to_bytes_v (ostree_repo_file_tree_get_contents_checksum (repo_root)),
                           ostree_checksum_to_bytes_v (ostree_repo_file_tree_get_metadata_checksum (repo_root)));
@@ -2073,21 +2099,13 @@ ostree_repo_read_commit_detached_metadata (OstreeRepo      *self,
   g_autoptr(GFile) metadata_path =
     _ostree_repo_get_commit_metadata_loose_path (self, checksum);
   g_autoptr(GVariant) ret_metadata = NULL;
-  GError *temp_error = NULL;
   
-  if (!ot_util_variant_map (metadata_path, G_VARIANT_TYPE ("a{sv}"),
-                            TRUE, &ret_metadata, &temp_error))
+  if (!ot_util_variant_map_at (AT_FDCWD, gs_file_get_path_cached (metadata_path),
+                               G_VARIANT_TYPE ("a{sv}"),
+                               OT_VARIANT_MAP_ALLOW_NOENT | OT_VARIANT_MAP_TRUSTED, &ret_metadata, error))
     {
-      if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
-        {
-          g_clear_error (&temp_error);
-        }
-      else
-        {
-          g_prefix_error (error, "Unable to read existing detached metadata: ");
-          g_propagate_error (error, temp_error);
-          goto out;
-        }
+      g_prefix_error (error, "Unable to read existing detached metadata: ");
+      goto out;
     }
 
   ret = TRUE;
@@ -2184,6 +2202,10 @@ create_tree_variant_from_hashes (GHashTable            *file_checksums,
   while (g_hash_table_iter_next (&hash_iter, &key, &value))
     {
       const char *name = key;
+
+      /* Should have been validated earlier, but be paranoid */
+      g_assert (ot_util_filename_validate (name, NULL));
+
       sorted_filenames = g_slist_prepend (sorted_filenames, (char*)name);
     }
 
@@ -2355,7 +2377,7 @@ get_modified_xattrs (OstreeRepo                       *self,
 
       if (label)
         {
-          GVariantBuilder *builder;
+          g_autoptr(GVariantBuilder) builder = NULL;
 
           /* ret_xattrs may be NULL */
           builder = ot_util_variant_builder_from_variant (ret_xattrs,
index fb651645bf9630d89eaf1c25047dd0e6f6dc0337..6f9b79fe1723d96fa3982b2ba350e806bd690145 100644 (file)
@@ -58,9 +58,6 @@ OstreeRepo * ostree_repo_file_get_repo (OstreeRepoFile  *self);
 _OSTREE_PUBLIC
 OstreeRepoFile * ostree_repo_file_get_root (OstreeRepoFile  *self);
 
-_OSTREE_PUBLIC
-void ostree_repo_file_make_empty_tree (OstreeRepoFile  *self);
-
 _OSTREE_PUBLIC
 void ostree_repo_file_tree_set_metadata (OstreeRepoFile *self,
                                           const char     *checksum,
index 7a30192c78e62da0333401f5031dbdf47155f650..45427ef774bda20be24abacce9feea7c88cf9ec8 100644 (file)
@@ -37,6 +37,8 @@
 
 #ifdef HAVE_LIBARCHIVE
 
+#define DEFAULT_DIRMODE (0755 | S_IFDIR)
+
 static void
 propagate_libarchive_error (GError      **error,
                             struct archive *a)
@@ -45,269 +47,761 @@ propagate_libarchive_error (GError      **error,
                "%s", archive_error_string (a));
 }
 
+static const char *
+path_relative (const char *src,
+               GError    **error)
+{
+  /* One issue here is that some archives almost record the pathname as just a
+   * string and don't need to actually encode parent/child relationships in the
+   * archive. For us however, this will be important. So we do our best to deal
+   * with non-conventional paths. We also validate the path at the end to make
+   * sure there are no illegal components. Also important, we relativize the
+   * path. */
+
+  /* relativize first (and make /../../ --> /) */
+  while (src[0] == '/')
+    {
+      src += 1;
+      if (src[0] == '.' && src[1] == '.' && src[2] == '/')
+        src += 2; /* keep trailing / so we continue */
+    }
+
+  /* now let's skip . and empty components */
+  while (TRUE)
+    {
+      if (src[0] == '.' && src[1] == '/')
+        src += 2;
+      else if (src[0] == '/')
+        src += 1;
+      else
+        break;
+    }
+
+  /* assume a single '.' means the root dir itself, which we handle as the empty
+   * string in our code */
+  if (src[0] == '.' && src[1] == '\0')
+    src += 1;
+
+  /* make sure that the final path is valid (no . or ..) */
+  if (!ot_util_path_split_validate (src, NULL, error))
+    {
+      g_prefix_error (error, "While making relative path \"%s\":", src);
+      return NULL;
+    }
+
+  return src;
+}
+
+static char *
+path_relative_ostree (const char *path,
+                      GError    **error)
+{
+  path = path_relative (path, error);
+  if (path == NULL)
+    return NULL;
+  if (g_str_has_prefix (path, "etc/"))
+    return g_strconcat ("usr/", path, NULL);
+  else if (strcmp (path, "etc") == 0)
+    return g_strdup ("usr/etc");
+  return g_strdup (path);
+}
+
+static void
+append_path_component (char       **path_builder,
+                       const char  *component)
+{
+  g_autofree char *s = g_steal_pointer (path_builder);
+  *path_builder = g_build_filename (s ?: "/", component, NULL);
+}
+
+/* inplace trailing slash squashing  */
+static void
+squash_trailing_slashes (char *path)
+{
+  char *endp = path + strlen (path) - 1;
+  for (; endp > path && *endp == '/'; endp--)
+    *endp = '\0';
+}
+
 static GFileInfo *
-file_info_from_archive_entry_and_modifier (OstreeRepo *repo,
-                                           struct archive_entry *entry,
-                                           OstreeRepoCommitModifier *modifier)
+file_info_from_archive_entry (struct archive_entry *entry)
 {
   g_autoptr(GFileInfo) info = NULL;
-  GFileInfo *modified_info = NULL;
-  const struct stat *st;
+  const struct stat *st = NULL;
   guint32 file_type;
+  mode_t mode;
 
   st = archive_entry_stat (entry);
+  mode = st->st_mode;
+
+  /* Some archives only store the permission mode bits in hardlink entries, so
+   * let's just make it into a regular file. Yes, this hack will work even if
+   * it's a hardlink to a symlink. */
+  if (archive_entry_hardlink (entry))
+    mode |= S_IFREG;
 
-  info = _ostree_header_gfile_info_new (st->st_mode, st->st_uid, st->st_gid);
-  file_type = ot_gfile_type_for_mode (st->st_mode);
+  info = _ostree_header_gfile_info_new (mode, st->st_uid, st->st_gid);
 
+  file_type = ot_gfile_type_for_mode (mode);
   if (file_type == G_FILE_TYPE_REGULAR)
     {
       g_file_info_set_attribute_uint64 (info, "standard::size", st->st_size);
     }
   else if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
     {
-      g_file_info_set_attribute_byte_string (info, "standard::symlink-target", archive_entry_symlink (entry));
+      g_file_info_set_attribute_byte_string (info, "standard::symlink-target",
+                                             archive_entry_symlink (entry));
     }
 
-  _ostree_repo_commit_modifier_apply (repo, modifier,
-                                      archive_entry_pathname (entry),
-                                      info, &modified_info);
-
-  return modified_info;
+  return g_steal_pointer (&info);
 }
 
 static gboolean
-import_libarchive_entry_file (OstreeRepo           *self,
-                              OstreeRepoImportArchiveOptions  *opts,
-                              struct archive       *a,
-                              struct archive_entry *entry,
-                              GFileInfo            *file_info,
-                              guchar              **out_csum,
-                              GCancellable         *cancellable,
-                              GError              **error)
+builder_add_label (GVariantBuilder  *builder,
+                   OstreeSePolicy   *sepolicy,
+                   const char       *path,
+                   mode_t            mode,
+                   GCancellable     *cancellable,
+                   GError          **error)
 {
-  gboolean ret = FALSE;
-  g_autoptr(GInputStream) file_object_input = NULL;
-  g_autoptr(GInputStream) archive_stream = NULL;
-  guint64 length;
-  
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+  g_autofree char *label = NULL;
+
+  if (!sepolicy)
+    return TRUE;
+
+  if (!ostree_sepolicy_get_label (sepolicy, path, mode, &label,
+                                  cancellable, error))
     return FALSE;
 
-  switch (g_file_info_get_file_type (file_info))
+  if (label)
+    g_variant_builder_add (builder, "(@ay@ay)",
+                           g_variant_new_bytestring ("security.selinux"),
+                           g_variant_new_bytestring (label));
+  return TRUE;
+}
+
+
+/* Like ostree_mutable_tree_ensure_dir(), but also creates and sets dirmeta if
+ * the dir has to be created. */
+static gboolean
+mtree_ensure_dir_with_meta (OstreeRepo          *repo,
+                            OstreeMutableTree   *parent,
+                            const char          *name,
+                            GFileInfo           *file_info,
+                            GVariant            *xattrs,
+                            gboolean             error_if_exist, /* XXX: remove if not needed */
+                            OstreeMutableTree  **out_dir,
+                            GCancellable        *cancellable,
+                            GError             **error)
+{
+  glnx_unref_object OstreeMutableTree *dir = NULL;
+  g_autofree guchar *csum_raw = NULL;
+  g_autofree char *csum = NULL;
+
+  if (name[0] == '\0') /* root? */
+    dir = g_object_ref (parent);
+  else if (ostree_mutable_tree_lookup (parent, name, NULL, &dir, error))
     {
-    case G_FILE_TYPE_REGULAR:
-      archive_stream = _ostree_libarchive_input_stream_new (a);
-      break;
-    case G_FILE_TYPE_SYMBOLIC_LINK:
-      break;
-    default:
-      if (opts->ignore_unsupported_content)
-        {
-          ret = TRUE;
-          goto out;
-        }
-      else
+      if (error_if_exist)
         {
           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Unable to import non-regular/non-symlink file '%s'",
-                       archive_entry_pathname (entry));
-          goto out;
+                       "Directory \"%s\" already exists", name);
+          return FALSE;
         }
     }
-  
-  if (!ostree_raw_file_to_content_stream (archive_stream, file_info, NULL,
-                                          &file_object_input, &length, cancellable, error))
-    goto out;
-  
-  if (!ostree_repo_write_content (self, NULL, file_object_input, length, out_csum,
+
+  if (dir == NULL)
+    {
+      if (!g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+        return FALSE;
+
+      g_clear_error (error);
+
+      if (!ostree_mutable_tree_ensure_dir (parent, name, &dir, error))
+        return FALSE;
+    }
+
+  if (!_ostree_repo_write_directory_meta (repo, file_info, xattrs,
+                                          &csum_raw, cancellable, error))
+    return FALSE;
+
+  csum = ostree_checksum_from_bytes (csum_raw);
+
+  ostree_mutable_tree_set_metadata_checksum (dir, csum);
+
+  if (out_dir)
+    *out_dir = g_steal_pointer (&dir);
+
+  return TRUE;
+}
+
+typedef struct {
+  OstreeRepo                     *repo;
+  OstreeRepoImportArchiveOptions *opts;
+  OstreeMutableTree              *root;
+  struct archive                 *archive;
+  struct archive_entry           *entry;
+  GHashTable                     *deferred_hardlinks;
+  OstreeRepoCommitModifier       *modifier;
+} OstreeRepoArchiveImportContext;
+
+typedef struct {
+  OstreeMutableTree  *parent;
+  char               *path;
+  guint64             size;
+} DeferredHardlink;
+
+static inline char*
+aic_get_final_path (OstreeRepoArchiveImportContext *ctx,
+                    const char  *path,
+                    GError     **error)
+{
+  if (ctx->opts->use_ostree_convention)
+    return path_relative_ostree (path, error);
+  return g_strdup (path_relative (path, error));
+}
+
+static inline char*
+aic_get_final_entry_pathname (OstreeRepoArchiveImportContext *ctx,
+                              GError  **error)
+{
+  const char *pathname = archive_entry_pathname (ctx->entry);
+  g_autofree char *final = aic_get_final_path (ctx, pathname, error);
+
+  if (final == NULL)
+    return NULL;
+
+  /* get rid of trailing slashes some archives put on dirs */
+  squash_trailing_slashes (final);
+  return g_steal_pointer (&final);
+}
+
+static inline char*
+aic_get_final_entry_hardlink (OstreeRepoArchiveImportContext *ctx)
+{
+  GError *local_error = NULL;
+  const char *hardlink = archive_entry_hardlink (ctx->entry);
+  g_autofree char *final = NULL;
+
+  if (hardlink != NULL)
+    {
+      final = aic_get_final_path (ctx, hardlink, &local_error);
+
+      /* hardlinks always point to a preceding entry, so if there were an error
+       * it would have failed then */
+      g_assert_no_error (local_error);
+    }
+
+  return g_steal_pointer (&final);
+}
+
+static OstreeRepoCommitFilterResult
+aic_apply_modifier_filter (OstreeRepoArchiveImportContext *ctx,
+                           const char  *relpath,
+                           GFileInfo  **out_file_info)
+{
+  g_autofree char *hardlink = aic_get_final_entry_hardlink (ctx);
+  g_autoptr(GFileInfo) file_info = NULL;
+  g_autofree char *abspath = NULL;
+  const char *cb_path = NULL;
+
+  if (ctx->opts->callback_with_entry_pathname)
+    cb_path = archive_entry_pathname (ctx->entry);
+  else
+    {
+      /* the user expects an abspath (where the dir to commit represents /) */
+      abspath = g_build_filename ("/", relpath, NULL);
+      cb_path = abspath;
+    }
+
+  file_info = file_info_from_archive_entry (ctx->entry);
+
+  return _ostree_repo_commit_modifier_apply (ctx->repo, ctx->modifier, cb_path,
+                                             file_info, out_file_info);
+}
+
+static gboolean
+aic_ensure_parent_dir_with_file_info (OstreeRepoArchiveImportContext *ctx,
+                                      OstreeMutableTree   *parent,
+                                      const char          *fullpath,
+                                      GFileInfo           *file_info,
+                                      OstreeMutableTree  **out_dir,
+                                      GCancellable        *cancellable,
+                                      GError             **error)
+{
+  const char *name = glnx_basename (fullpath);
+  g_auto(GVariantBuilder) xattrs_builder;
+  g_autoptr(GVariant) xattrs = NULL;
+
+  /* is this the root directory itself? transform into empty string */
+  if (name[0] == '/' && name[1] == '\0')
+    name++;
+
+  g_variant_builder_init (&xattrs_builder, (GVariantType*)"a(ayay)");
+
+  if (ctx->modifier && ctx->modifier->sepolicy)
+    if (!builder_add_label (&xattrs_builder, ctx->modifier->sepolicy, fullpath,
+                            DEFAULT_DIRMODE, cancellable, error))
+      return FALSE;
+
+  xattrs = g_variant_ref_sink (g_variant_builder_end (&xattrs_builder));
+  return mtree_ensure_dir_with_meta (ctx->repo, parent, name, file_info,
+                                     xattrs,
+                                     FALSE /* error_if_exist */, out_dir,
+                                     cancellable, error);
+}
+
+static gboolean
+aic_ensure_parent_dir (OstreeRepoArchiveImportContext *ctx,
+                       OstreeMutableTree   *parent,
+                       const char          *fullpath,
+                       OstreeMutableTree  **out_dir,
+                       GCancellable        *cancellable,
+                       GError             **error)
+{
+  /* Who should own the parent dir? Since it's not in the archive, it's up to
+   * us. Here, we use the heuristic of simply creating it as the same user as
+   * the owner of the archive entry for which we're creating the dir. This is OK
+   * since any nontrivial dir perms should have explicit archive entries. */
+
+  guint32 uid = archive_entry_uid (ctx->entry);
+  guint32 gid = archive_entry_gid (ctx->entry);
+  glnx_unref_object GFileInfo *file_info = g_file_info_new ();
+
+  g_file_info_set_attribute_uint32 (file_info, "unix::uid", uid);
+  g_file_info_set_attribute_uint32 (file_info, "unix::gid", gid);
+  g_file_info_set_attribute_uint32 (file_info, "unix::mode", DEFAULT_DIRMODE);
+
+  return aic_ensure_parent_dir_with_file_info (ctx, parent, fullpath, file_info,
+                                               out_dir, cancellable, error);
+}
+
+static gboolean
+aic_create_parent_dirs (OstreeRepoArchiveImportContext *ctx,
+                        GPtrArray           *components,
+                        OstreeMutableTree  **out_subdir,
+                        GCancellable        *cancellable,
+                        GError             **error)
+{
+  g_autofree char *fullpath = NULL;
+  glnx_unref_object OstreeMutableTree *dir = NULL;
+
+  /* start with the root itself */
+  if (!aic_ensure_parent_dir (ctx, ctx->root, "/", &dir, cancellable, error))
+    return FALSE;
+
+  for (guint i = 0; i < components->len-1; i++)
+    {
+      glnx_unref_object OstreeMutableTree  *subdir = NULL;
+      append_path_component (&fullpath, components->pdata[i]);
+
+      if (!aic_ensure_parent_dir (ctx, dir, fullpath, &subdir,
                                   cancellable, error))
-    goto out;
+        return FALSE;
 
-  ret = TRUE;
- out:
-  return ret;
+      g_set_object (&dir, subdir);
+    }
+
+  *out_subdir = g_steal_pointer (&dir);
+  return TRUE;
 }
 
 static gboolean
-write_libarchive_entry_to_mtree (OstreeRepo           *self,
-                                 OstreeRepoImportArchiveOptions  *opts,
-                                 OstreeMutableTree    *root,
-                                 struct archive       *a,
-                                 struct archive_entry *entry,
-                                 OstreeRepoCommitModifier *modifier,
-                                 const guchar         *tmp_dir_csum,
-                                 GCancellable         *cancellable,
-                                 GError              **error)
+aic_get_parent_dir (OstreeRepoArchiveImportContext *ctx,
+                    const char          *path,
+                    OstreeMutableTree  **out_dir,
+                    GCancellable        *cancellable,
+                    GError             **error)
 {
-  gboolean ret = FALSE;
-  const char *pathname;
-  const char *hardlink;
-  const char *basename;
-  g_autoptr(GFileInfo) file_info = NULL;
-  g_autoptr(GPtrArray) split_path = NULL;
-  g_autoptr(GPtrArray) hardlink_split_path = NULL;
-  glnx_unref_object OstreeMutableTree *subdir = NULL;
-  glnx_unref_object OstreeMutableTree *parent = NULL;
-  glnx_unref_object OstreeMutableTree *hardlink_source_parent = NULL;
-  g_autofree char *hardlink_source_checksum = NULL;
-  glnx_unref_object OstreeMutableTree *hardlink_source_subdir = NULL;
-  g_autofree guchar *tmp_csum = NULL;
-  g_autofree char *tmp_checksum = NULL;
+  g_autoptr(GPtrArray) components = NULL;
+  if (!ot_util_path_split_validate (path, &components, error))
+    return FALSE;
 
-  pathname = archive_entry_pathname (entry); 
-      
-  if (!ot_util_path_split_validate (pathname, &split_path, error))
-    goto out;
+  if (components->len == 0) /* root dir? */
+    {
+      *out_dir = g_object_ref (ctx->root);
+      return TRUE;
+    }
+
+  if (ostree_mutable_tree_walk (ctx->root, components, 0, out_dir, error))
+    return TRUE; /* already exists, nice! */
 
-  if (split_path->len == 0)
+  if (!g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+    return FALSE; /* some other error occurred */
+
+  if (ctx->opts->autocreate_parents)
     {
-      parent = NULL;
-      basename = NULL;
+      g_clear_error (error);
+      return aic_create_parent_dirs (ctx, components, out_dir,
+                                     cancellable, error);
     }
-  else
+
+  return FALSE;
+}
+
+static gboolean
+aic_get_xattrs (OstreeRepoArchiveImportContext *ctx,
+                const char         *path,
+                GFileInfo          *file_info,
+                GVariant          **out_xattrs,
+                GCancellable       *cancellable,
+                GError            **error)
+{
+  g_autofree char *abspath = g_build_filename ("/", path, NULL);
+  g_autoptr(GVariant) xattrs = NULL;
+  const char *cb_path = abspath;
+
+  if (ctx->opts->callback_with_entry_pathname)
+    cb_path = archive_entry_pathname (ctx->entry);
+
+  if (ctx->modifier && ctx->modifier->xattr_callback)
+    xattrs = ctx->modifier->xattr_callback (ctx->repo, cb_path, file_info,
+                                            ctx->modifier->xattr_user_data);
+
+  if (ctx->modifier && ctx->modifier->sepolicy)
     {
-      if (tmp_dir_csum)
-        {
-          g_free (tmp_checksum);
-          tmp_checksum = ostree_checksum_from_bytes (tmp_dir_csum);
-          if (!ostree_mutable_tree_ensure_parent_dirs (root, split_path,
-                                                       tmp_checksum,
-                                                       &parent,
-                                                       error))
-            goto out;
-        }
-      else
-        {
-          if (!ostree_mutable_tree_walk (root, split_path, 0, &parent, error))
-            goto out;
-        }
-      basename = (char*)split_path->pdata[split_path->len-1];
+      mode_t mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
+      g_autoptr(GVariantBuilder) builder =
+        ot_util_variant_builder_from_variant (xattrs, G_VARIANT_TYPE
+                                                        ("a(ayay)"));
+
+      if (!builder_add_label (builder, ctx->modifier->sepolicy, abspath, mode,
+                              cancellable, error))
+        return FALSE;
+
+      if (xattrs)
+        g_variant_unref (xattrs);
+
+      xattrs = g_variant_builder_end (builder);
+      g_variant_ref_sink (xattrs);
     }
 
-  hardlink = archive_entry_hardlink (entry);
+  *out_xattrs = g_steal_pointer (&xattrs);
+  return TRUE;
+}
+
+/* XXX: add option in ctx->opts to disallow already existing dirs? see
+ * error_if_exist */
+static gboolean
+aic_handle_dir (OstreeRepoArchiveImportContext *ctx,
+                OstreeMutableTree  *parent,
+                const char         *path,
+                GFileInfo          *fi,
+                GCancellable       *cancellable,
+                GError            **error)
+{
+  const char *name = glnx_basename (path);
+  g_autoptr(GVariant) xattrs = NULL;
+
+  if (!aic_get_xattrs (ctx, path, fi, &xattrs, cancellable, error))
+    return FALSE;
+
+  return mtree_ensure_dir_with_meta (ctx->repo, parent, name, fi, xattrs,
+                                     FALSE /* error_if_exist */, NULL,
+                                     cancellable, error);
+}
+
+static gboolean
+aic_write_file (OstreeRepoArchiveImportContext *ctx,
+                GFileInfo          *fi,
+                GVariant           *xattrs,
+                char              **out_csum,
+                GCancellable       *cancellable,
+                GError            **error)
+{
+  g_autoptr(GInputStream) archive_stream = NULL;
+  g_autoptr(GInputStream) file_object_input = NULL;
+  guint64 length;
+
+  g_autofree guchar *csum_raw = NULL;
+
+  if (g_file_info_get_file_type (fi) == G_FILE_TYPE_REGULAR)
+    archive_stream = _ostree_libarchive_input_stream_new (ctx->archive);
+
+  if (!ostree_raw_file_to_content_stream (archive_stream, fi, xattrs,
+                                          &file_object_input, &length,
+                                          cancellable, error))
+    return FALSE;
+
+  if (!ostree_repo_write_content (ctx->repo, NULL, file_object_input, length,
+                                  &csum_raw, cancellable, error))
+    return FALSE;
+
+  *out_csum = ostree_checksum_from_bytes (csum_raw);
+  return TRUE;
+}
+
+static gboolean
+aic_import_file (OstreeRepoArchiveImportContext *ctx,
+                 OstreeMutableTree  *parent,
+                 const char         *path,
+                 GFileInfo          *fi,
+                 GCancellable       *cancellable,
+                 GError            **error)
+{
+  const char *name = glnx_basename (path);
+  g_autoptr(GVariant) xattrs = NULL;
+  g_autofree char *csum = NULL;
+
+  if (!aic_get_xattrs (ctx, path, fi, &xattrs, cancellable, error))
+    return FALSE;
+
+  if (!aic_write_file (ctx, fi, xattrs, &csum, cancellable, error))
+    return FALSE;
+
+  if (!ostree_mutable_tree_replace_file (parent, name, csum, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+aic_add_deferred_hardlink (OstreeRepoArchiveImportContext *ctx,
+                           const char        *hardlink,
+                           DeferredHardlink  *dh)
+{
+  gboolean new_slist;
+  GSList *slist;
+
+  slist = g_hash_table_lookup (ctx->deferred_hardlinks, hardlink);
+  new_slist = (slist == NULL);
+
+  slist = g_slist_append (slist, dh);
+
+  if (new_slist)
+    g_hash_table_insert (ctx->deferred_hardlinks, g_strdup (hardlink), slist);
+}
+
+static void
+aic_defer_hardlink (OstreeRepoArchiveImportContext *ctx,
+                    OstreeMutableTree  *parent,
+                    const char         *path,
+                    guint64             size,
+                    const char         *hardlink)
+{
+  DeferredHardlink *dh = g_slice_new (DeferredHardlink);
+  dh->parent = g_object_ref (parent);
+  dh->path = g_strdup (path);
+  dh->size = size;
+
+  aic_add_deferred_hardlink (ctx, hardlink, dh);
+}
+
+static gboolean
+aic_handle_file (OstreeRepoArchiveImportContext *ctx,
+                 OstreeMutableTree  *parent,
+                 const char         *path,
+                 GFileInfo          *fi,
+                 GCancellable       *cancellable,
+                 GError            **error)
+{
+  /* The wonderful world of hardlinks and archives. We have to be very careful
+   * here. Do not assume that if a file is a hardlink, it will have size 0 (e.g.
+   * cpio). Do not assume that if a file will have hardlinks to it, it will have
+   * size > 0. Also do not assume that its nlink param is present (tar) or even
+   * accurate (cpio). Also do not assume that hardlinks follow each other in
+   * order of entries.
+   *
+   * These archives were made to be extracted onto a filesystem, not directly
+   * hashed into an object store. So to be careful, we defer all hardlink
+   * imports until the very end. Nonzero files have to be imported, hardlink or
+   * not, since we can't easily seek back to this position later on.
+   * */
+
+  g_autofree char *hardlink = aic_get_final_entry_hardlink (ctx);
+  guint64 size = g_file_info_get_attribute_uint64 (fi, "standard::size");
+
+  if (hardlink == NULL || size > 0)
+    if (!aic_import_file (ctx, parent, path, fi, cancellable, error))
+      return FALSE;
+
   if (hardlink)
-    {
-      const char *hardlink_basename;
-      
-      g_assert (parent != NULL);
+    aic_defer_hardlink (ctx, parent, path, size, hardlink);
 
-      if (!ot_util_path_split_validate (hardlink, &hardlink_split_path, error))
-        goto out;
-      if (hardlink_split_path->len == 0)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Invalid hardlink path %s", hardlink);
-          goto out;
-        }
-      
-      hardlink_basename = hardlink_split_path->pdata[hardlink_split_path->len - 1];
-      
-      if (!ostree_mutable_tree_walk (root, hardlink_split_path, 0, &hardlink_source_parent, error))
-        goto out;
-      
-      if (!ostree_mutable_tree_lookup (hardlink_source_parent, hardlink_basename,
-                                       &hardlink_source_checksum,
-                                       &hardlink_source_subdir,
-                                       error))
-        {
-              g_prefix_error (error, "While resolving hardlink target: ");
-              goto out;
-        }
-      
-      if (hardlink_source_subdir)
+  return TRUE;
+}
+
+static gboolean
+aic_handle_entry (OstreeRepoArchiveImportContext *ctx,
+                  OstreeMutableTree  *parent,
+                  const char         *path,
+                  GFileInfo          *fi,
+                  GCancellable       *cancellable,
+                  GError            **error)
+{
+  switch (g_file_info_get_file_type (fi))
+    {
+    case G_FILE_TYPE_DIRECTORY:
+      return aic_handle_dir (ctx, parent, path, fi, cancellable, error);
+    case G_FILE_TYPE_REGULAR:
+    case G_FILE_TYPE_SYMBOLIC_LINK:
+      return aic_handle_file (ctx, parent, path, fi, cancellable, error);
+    default:
+      if (ctx->opts->ignore_unsupported_content)
+        return TRUE;
+      else
         {
           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Hardlink %s refers to directory %s",
-                       pathname, hardlink);
-          goto out;
+                       "Unsupported file type for path \"%s\"", path);
+          return FALSE;
         }
-      g_assert (hardlink_source_checksum);
-      
-      if (!ostree_mutable_tree_replace_file (parent,
-                                             basename,
-                                             hardlink_source_checksum,
-                                             error))
-        goto out;
     }
-  else
+}
+
+static gboolean
+aic_import_entry (OstreeRepoArchiveImportContext *ctx,
+                  GCancellable  *cancellable,
+                  GError       **error)
+{
+  g_autoptr(GFileInfo) fi = NULL;
+  glnx_unref_object OstreeMutableTree *parent = NULL;
+  g_autofree char *path = aic_get_final_entry_pathname (ctx, error);
+
+  if (path == NULL)
+    return FALSE;
+
+  if (aic_apply_modifier_filter (ctx, path, &fi)
+        == OSTREE_REPO_COMMIT_FILTER_SKIP)
+    return TRUE;
+
+  if (!aic_get_parent_dir (ctx, path, &parent, cancellable, error))
+    return FALSE;
+
+  return aic_handle_entry (ctx, parent, path, fi, cancellable, error);
+}
+
+static gboolean
+aic_import_from_hardlink (OstreeRepoArchiveImportContext *ctx,
+                          const char        *target,
+                          DeferredHardlink  *dh,
+                          GError           **error)
+{
+  g_autofree char *csum = NULL;
+  const char *name = glnx_basename (target);
+  const char *name_dh = glnx_basename (dh->path);
+  g_autoptr(GPtrArray) components = NULL;
+  glnx_unref_object OstreeMutableTree *parent = NULL;
+
+  if (!ostree_mutable_tree_lookup (dh->parent, name_dh, &csum, NULL, error))
+    return FALSE;
+
+  g_assert (csum);
+
+  if (!ot_util_path_split_validate (target, &components, error))
+    return FALSE;
+
+  if (!ostree_mutable_tree_walk (ctx->root, components, 0, &parent, error))
+    return FALSE;
+
+  if (!ostree_mutable_tree_replace_file (parent, name, csum, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+aic_lookup_file_csum (OstreeRepoArchiveImportContext *ctx,
+                      const char    *target,
+                      char         **out_csum,
+                      GError       **error)
+{
+  g_autofree char *csum = NULL;
+  const char *name = glnx_basename (target);
+  glnx_unref_object OstreeMutableTree *parent = NULL;
+  glnx_unref_object OstreeMutableTree *subdir = NULL;
+  g_autoptr(GPtrArray) components = NULL;
+
+  if (!ot_util_path_split_validate (target, &components, error))
+    return FALSE;
+
+  if (!ostree_mutable_tree_walk (ctx->root, components, 0, &parent, error))
+    return FALSE;
+
+  if (!ostree_mutable_tree_lookup (parent, name, &csum, &subdir, error))
+    return FALSE;
+
+  if (subdir != NULL)
     {
-      file_info = file_info_from_archive_entry_and_modifier (self, entry, modifier);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Expected hardlink file target at \"%s\" but found a "
+                   "directory", target);
+      return FALSE;
+    }
 
-      if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_UNKNOWN)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Unsupported file for import: %s", pathname);
-          goto out;
-        }
+  *out_csum = g_steal_pointer (&csum);
+  return TRUE;
+}
 
-      if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
-        {
+static gboolean
+aic_import_deferred_hardlinks_for (OstreeRepoArchiveImportContext *ctx,
+                                   const char    *target,
+                                   GSList        *hardlinks,
+                                   GError       **error)
+{
+  GSList *payload = hardlinks;
+  g_autofree char *csum = NULL;
 
-          if (!_ostree_repo_write_directory_meta (self, file_info, NULL, &tmp_csum, cancellable, error))
-            goto out;
+  /* find node with the payload, if any (if none, then they're all hardlinks to
+   * a zero sized target, and there's no rewrite required) */
+  while (payload && ((DeferredHardlink*)payload->data)->size == 0)
+    payload = g_slist_next (payload);
 
-          if (parent == NULL)
-            {
-              subdir = g_object_ref (root);
-            }
-          else
-            {
-              if (!ostree_mutable_tree_ensure_dir (parent, basename, &subdir, error))
-                goto out;
-            }
+  /* rewrite the target so it points to the csum of the payload hardlink */
+  if (payload)
+    if (!aic_import_from_hardlink (ctx, target, payload->data, error))
+      return FALSE;
 
-          g_free (tmp_checksum);
-          tmp_checksum = ostree_checksum_from_bytes (tmp_csum);
-          ostree_mutable_tree_set_metadata_checksum (subdir, tmp_checksum);
-        }
-      else 
-        {
-          if (parent == NULL)
-            {
-              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "Can't import file as root");
-              goto out;
-            }
+  if (!aic_lookup_file_csum (ctx, target, &csum, error))
+    return FALSE;
 
-          if (!import_libarchive_entry_file (self, opts, a, entry, file_info, &tmp_csum,
-                                             cancellable, error))
-            goto out;
+  /* import all the hardlinks */
+  for (GSList *hl = hardlinks; hl != NULL; hl = g_slist_next (hl))
+    {
+      DeferredHardlink *df = hl->data;
+      const char *name = glnx_basename (df->path);
 
-          if (tmp_csum)
-            { 
-              g_free (tmp_checksum);
-              tmp_checksum = ostree_checksum_from_bytes (tmp_csum);
-              if (!ostree_mutable_tree_replace_file (parent, basename,
-                                                     tmp_checksum,
-                                                     error))
-                goto out;
-            }
-        }
+      if (hl == payload)
+        continue; /* small optimization; no need to redo this one */
+
+      if (!ostree_mutable_tree_replace_file (df->parent, name, csum, error))
+        return FALSE;
     }
 
-  ret = TRUE;
- out:
-  return ret;
+  return TRUE;
 }
 
 static gboolean
-create_empty_dir_with_uidgid (OstreeRepo   *self,
-                              guint32       uid,
-                              guint32       gid,
-                              guint8      **out_csum,
-                              GCancellable *cancellable,
-                              GError      **error)
-{
-  g_autoptr(GFileInfo) tmp_dir_info = g_file_info_new ();
-          
-  g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::uid", uid);
-  g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::gid", gid);
-  g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::mode", 0755 | S_IFDIR);
-  
-  return _ostree_repo_write_directory_meta (self, tmp_dir_info, NULL, out_csum, cancellable, error);
+aic_import_deferred_hardlinks (OstreeRepoArchiveImportContext *ctx,
+                               GCancellable  *cancellable,
+                               GError       **error)
+{
+  GHashTableIter iter;
+  gpointer key, value;
+
+  g_hash_table_iter_init (&iter, ctx->deferred_hardlinks);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    if (!aic_import_deferred_hardlinks_for (ctx, key, value, error))
+      return FALSE;
+
+  return TRUE;
 }
-#endif
+
+static void
+deferred_hardlink_free (void *data)
+{
+  DeferredHardlink *dh = data;
+  g_object_unref (dh->parent);
+  g_free (dh->path);
+  g_slice_free (DeferredHardlink, dh);
+}
+
+static void
+deferred_hardlinks_list_free (void *data)
+{
+  GSList *slist = data;
+  g_slist_free_full (slist, deferred_hardlink_free);
+}
+#endif /* HAVE_LIBARCHIVE */
 
 /**
  * ostree_repo_import_archive_to_mtree:
@@ -334,61 +828,56 @@ ostree_repo_import_archive_to_mtree (OstreeRepo                   *self,
 #ifdef HAVE_LIBARCHIVE
   gboolean ret = FALSE;
   struct archive *a = archive;
-  struct archive_entry *entry;
-  g_autofree guchar *tmp_csum = NULL;
-  int r;
-
+  g_autoptr(GHashTable) deferred_hardlinks =
+    g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+                           deferred_hardlinks_list_free);
+
+  OstreeRepoArchiveImportContext aictx = {
+    .repo = self,
+    .opts = opts,
+    .root = mtree,
+    .archive = archive,
+    .deferred_hardlinks = deferred_hardlinks,
+    .modifier = modifier
+  };
 
   while (TRUE)
     {
-      r = archive_read_next_header (a, &entry);
+      int r = archive_read_next_header (a, &aictx.entry);
       if (r == ARCHIVE_EOF)
         break;
-      else if (r != ARCHIVE_OK)
+      if (r != ARCHIVE_OK)
         {
           propagate_libarchive_error (error, a);
           goto out;
         }
 
-      /* TODO - refactor this to only create the metadata on demand
-       * (i.e. if there is a missing parent dir)
-       */
-      if (opts->autocreate_parents && !tmp_csum)
-        {
-          /* Here, we auto-pick the first uid/gid we find in the
-           * archive.  Realistically this is probably always going to
-           * be root, but eh, at least we try to match.
-           */
-          if (!create_empty_dir_with_uidgid (self, archive_entry_uid (entry),
-                                             archive_entry_gid (entry),
-                                             &tmp_csum, cancellable, error))
-            goto out;
-        }
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        goto out;
 
-      if (!write_libarchive_entry_to_mtree (self, opts, mtree, a,
-                                            entry, modifier, tmp_csum,
-                                            cancellable, error))
+      if (!aic_import_entry (&aictx, cancellable, error))
         goto out;
     }
 
+  if (!aic_import_deferred_hardlinks (&aictx, cancellable, error))
+    goto out;
+
   /* If we didn't import anything at all, and autocreation of parents
    * is enabled, automatically create a root directory.  This is
    * useful primarily when importing Docker image layers, which can
    * just be metadata.
    */
-  if (!ostree_mutable_tree_get_metadata_checksum (mtree) && opts->autocreate_parents)
+  if (opts->autocreate_parents &&
+      ostree_mutable_tree_get_metadata_checksum (mtree) == NULL)
     {
-      char tmp_checksum[65];
+      glnx_unref_object GFileInfo *fi = g_file_info_new ();
+      g_file_info_set_attribute_uint32 (fi, "unix::uid", 0);
+      g_file_info_set_attribute_uint32 (fi, "unix::gid", 0);
+      g_file_info_set_attribute_uint32 (fi, "unix::mode", DEFAULT_DIRMODE);
 
-      if (!tmp_csum)
-        {
-          /* We didn't have any archive entries to match, so pick uid 0, gid 0. */
-          if (!create_empty_dir_with_uidgid (self, 0, 0, &tmp_csum, cancellable, error))
-            goto out;
-        }
-      
-      ostree_checksum_inplace_from_bytes (tmp_csum, tmp_checksum);
-      ostree_mutable_tree_set_metadata_checksum (mtree, tmp_checksum);
+      if (!aic_ensure_parent_dir_with_file_info (&aictx, mtree, "/", fi, NULL,
+                                                 cancellable, error))
+        goto out;
     }
 
   ret = TRUE;
@@ -400,7 +889,7 @@ ostree_repo_import_archive_to_mtree (OstreeRepo                   *self,
   return FALSE;
 #endif
 }
-                          
+
 /**
  * ostree_repo_write_archive_to_mtree:
  * @self: An #OstreeRepo
@@ -420,8 +909,8 @@ ostree_repo_write_archive_to_mtree (OstreeRepo                *self,
                                     OstreeMutableTree         *mtree,
                                     OstreeRepoCommitModifier  *modifier,
                                     gboolean                   autocreate_parents,
-                                    GCancellable             *cancellable,
-                                    GError                  **error)
+                                    GCancellable              *cancellable,
+                                    GError                   **error)
 {
 #ifdef HAVE_LIBARCHIVE
   gboolean ret = FALSE;
@@ -480,16 +969,22 @@ file_to_archive_entry_common (GFile         *root,
   g_autoptr(GVariant) xattrs = NULL;
   time_t ts = (time_t) opts->timestamp_secs;
 
-  if (pathstr && !pathstr[0])
+  if (opts->path_prefix && opts->path_prefix[0])
+    {
+      g_autofree char *old_pathstr = pathstr;
+      pathstr = g_strconcat (opts->path_prefix, old_pathstr, NULL);
+    }
+
+  if (pathstr == NULL || !pathstr[0])
     {
       g_free (pathstr);
       pathstr = g_strdup (".");
     }
 
   archive_entry_update_pathname_utf8 (entry, pathstr);
-  archive_entry_set_ctime (entry, ts, 0);
-  archive_entry_set_mtime (entry, ts, 0);
-  archive_entry_set_atime (entry, ts, 0);
+  archive_entry_set_ctime (entry, ts, OSTREE_TIMESTAMP);
+  archive_entry_set_mtime (entry, ts, OSTREE_TIMESTAMP);
+  archive_entry_set_atime (entry, ts, OSTREE_TIMESTAMP);
   archive_entry_set_uid (entry, g_file_info_get_attribute_uint32 (file_info, "unix::uid"));
   archive_entry_set_gid (entry, g_file_info_get_attribute_uint32 (file_info, "unix::gid"));
   archive_entry_set_mode (entry, g_file_info_get_attribute_uint32 (file_info, "unix::mode"));
index 6a9092e94c4a112505c4362d212092ceb83dd2ab..f330d16927cd877962a07636942d802d0d3865f2 100644 (file)
 #include "ostree-repo.h"
 #include "libglnx.h"
 
-#ifdef HAVE_LIBSOUP
-#include "ostree-fetcher.h"
-#endif
-
 G_BEGIN_DECLS
 
 #define OSTREE_DELTAPART_VERSION (0)
@@ -36,6 +32,28 @@ G_BEGIN_DECLS
 #define _OSTREE_SUMMARY_CACHE_DIR "summaries"
 #define _OSTREE_CACHE_DIR "cache"
 
+#define OSTREE_TIMESTAMP (1)
+
+typedef enum {
+  OSTREE_REPO_TEST_ERROR_PRE_COMMIT = (1 << 0)
+} OstreeRepoTestErrorFlags;
+
+struct OstreeRepoCommitModifier {
+  volatile gint refcount;
+
+  OstreeRepoCommitModifierFlags flags;
+  OstreeRepoCommitFilter filter;
+  gpointer user_data;
+  GDestroyNotify destroy_notify;
+
+  OstreeRepoCommitModifierXattrCallback xattr_callback;
+  GDestroyNotify xattr_destroy;
+  gpointer xattr_user_data;
+
+  OstreeSePolicy *sepolicy;
+  GHashTable *devino_cache;
+};
+
 /**
  * OstreeRepo:
  *
@@ -44,7 +62,7 @@ G_BEGIN_DECLS
 struct OstreeRepo {
   GObject parent;
 
-  char *boot_id;
+  char *stagedir_prefix;
   int commit_stagedir_fd;
   char *commit_stagedir_name;
   GLnxLockFile commit_stagedir_lock;
@@ -86,12 +104,15 @@ struct OstreeRepo {
   uid_t target_owner_uid;
   gid_t target_owner_gid;
 
+  guint test_error_flags; /* OstreeRepoTestErrorFlags */
+
   GKeyFile *config;
   GHashTable *remotes;
   GMutex remotes_lock;
   OstreeRepoMode mode;
   gboolean enable_uncompressed_cache;
   gboolean generate_sizes;
+  guint64 tmp_expiry_seconds;
 
   OstreeRepo *parent_repo;
 };
@@ -102,6 +123,9 @@ typedef struct {
   char checksum[65];
 } OstreeDevIno;
 
+#define OSTREE_REPO_TMPDIR_STAGING "staging-"
+#define OSTREE_REPO_TMPDIR_FETCHER "fetcher-"
+
 gboolean
 _ostree_repo_allocate_tmpdir (int           tmpdir_dfd,
                               const char   *tmpdir_prefix,
@@ -112,6 +136,16 @@ _ostree_repo_allocate_tmpdir (int           tmpdir_dfd,
                               GCancellable *cancellable,
                               GError      **error);
 
+gboolean
+_ostree_repo_is_locked_tmpdir (const char *filename);
+
+gboolean
+_ostree_repo_try_lock_tmpdir (int            tmpdir_dfd,
+                              const char    *tmpdir_name,
+                              GLnxLockFile  *file_lock_out,
+                              gboolean      *out_did_lock,
+                              GError       **error);
+
 gboolean
 _ostree_repo_ensure_loose_objdir_at (int             dfd,
                                      const char     *loose_path,
@@ -135,8 +169,6 @@ _ostree_repo_has_loose_object (OstreeRepo           *self,
                                const char           *checksum,
                                OstreeObjectType      objtype,
                                gboolean             *out_is_stored,
-                               char                 *loose_path_buf,
-                               GFile               **out_stored_path,
                                GCancellable         *cancellable,
                                GError             **error);
 
@@ -199,13 +231,6 @@ _ostree_repo_commit_modifier_apply (OstreeRepo               *self,
 gboolean
 _ostree_repo_remote_name_is_file (const char *remote_name);
 
-#ifdef HAVE_LIBSOUP
-OstreeFetcher *
-_ostree_repo_remote_new_fetcher (OstreeRepo  *self,
-                                 const char  *remote_name,
-                                 GError     **error);
-#endif
-
 OstreeGpgVerifyResult *
 _ostree_repo_gpg_verify_with_metadata (OstreeRepo          *self,
                                        GBytes              *signed_data,
@@ -293,22 +318,4 @@ gboolean
 _ostree_repo_update_mtime (OstreeRepo        *self,
                            GError           **error);
 
-/* Load the summary from the cache if the provided .sig file is the same as the
-   cached version.  */
-gboolean
-_ostree_repo_load_cache_summary_if_same_sig (OstreeRepo        *self,
-                                             const char        *remote,
-                                             GBytes            *summary_sig,
-                                             GBytes            **summary,
-                                             GCancellable      *cancellable,
-                                             GError           **error);
-
-gboolean
-_ostree_repo_cache_summary (OstreeRepo        *self,
-                            const char        *remote,
-                            GBytes            *summary,
-                            GBytes            *summary_sig,
-                            GCancellable      *cancellable,
-                            GError           **error);
-
 G_END_DECLS
index eef5f039979bd74cb1881d0668437ec324444730..53847340cf227267aae8e31183132f80afda5d8e 100644 (file)
 #include "config.h"
 
 #include "ostree.h"
+#include "otutil.h"
+
+#ifdef HAVE_LIBSOUP
+
+#include "libglnx.h"
 #include "ostree-core-private.h"
 #include "ostree-repo-private.h"
 #include "ostree-repo-static-delta-private.h"
 #include "ostree-metalink.h"
-#include "otutil.h"
 #include "ot-fs-utils.h"
 
 #include <gio/gunixinputstream.h>
@@ -629,6 +633,7 @@ content_fetch_on_write_complete (GObject        *object,
   const char *expected_checksum;
   g_autofree guchar *csum = NULL;
   g_autofree char *checksum = NULL;
+  g_autofree char *checksum_obj = NULL;
 
   if (!ostree_repo_write_content_finish ((OstreeRepo*)object, result, 
                                          &csum, error))
@@ -639,7 +644,8 @@ content_fetch_on_write_complete (GObject        *object,
   ostree_object_name_deserialize (fetch_data->object, &expected_checksum, &objtype);
   g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
 
-  g_debug ("write of %s complete", ostree_object_to_string (checksum, objtype));
+  checksum_obj = ostree_object_to_string (checksum, objtype);
+  g_debug ("write of %s complete", checksum_obj);
 
   if (strcmp (checksum, expected_checksum) != 0)
     {
@@ -675,6 +681,7 @@ content_fetch_on_complete (GObject        *object,
   g_autoptr(GInputStream) object_input = NULL;
   g_autofree char *temp_path = NULL;
   const char *checksum;
+  g_autofree char *checksum_obj = NULL;
   OstreeObjectType objtype;
 
   temp_path = _ostree_fetcher_request_uri_with_partial_finish (fetcher, result, error);
@@ -684,7 +691,8 @@ content_fetch_on_complete (GObject        *object,
   ostree_object_name_deserialize (fetch_data->object, &checksum, &objtype);
   g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
 
-  g_debug ("fetch of %s complete", ostree_object_to_string (checksum, objtype));
+  checksum_obj = ostree_object_to_string (checksum, objtype);
+  g_debug ("fetch of %s complete", checksum_obj);
 
   if (pull_data->is_mirror && pull_data->repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2)
     {
@@ -796,13 +804,16 @@ meta_fetch_on_complete (GObject           *object,
   g_autoptr(GVariant) metadata = NULL;
   g_autofree char *temp_path = NULL;
   const char *checksum;
+  g_autofree char *checksum_obj = NULL;
   OstreeObjectType objtype;
   GError *local_error = NULL;
   GError **error = &local_error;
   glnx_fd_close int fd = -1;
+  gboolean free_fetch_data = FALSE;
 
   ostree_object_name_deserialize (fetch_data->object, &checksum, &objtype);
-  g_debug ("fetch of %s%s complete", ostree_object_to_string (checksum, objtype),
+  checksum_obj = ostree_object_to_string (checksum, objtype);
+  g_debug ("fetch of %s%s complete", checksum_obj,
            fetch_data->is_detached_meta ? " (detached)" : "");
 
   temp_path = _ostree_fetcher_request_uri_with_partial_finish (fetcher, result, error);
@@ -864,6 +875,8 @@ meta_fetch_on_complete (GObject           *object,
 
       if (!fetch_data->object_is_stored)
         enqueue_one_object_request (pull_data, checksum, objtype, FALSE, FALSE);
+
+      free_fetch_data = TRUE;
     }
   else
     {
@@ -901,7 +914,7 @@ meta_fetch_on_complete (GObject           *object,
   pull_data->n_outstanding_metadata_fetches--;
   pull_data->n_fetched_metadata++;
   check_outstanding_requests_handle_error (pull_data, local_error);
-  if (local_error)
+  if (local_error || free_fetch_data)
     {
       g_variant_unref (fetch_data->object);
       g_free (fetch_data);
@@ -1445,6 +1458,7 @@ request_static_delta_superblock_sync (OtPullData  *pull_data,
   if (out_delta_superblock)
     *out_delta_superblock = g_steal_pointer (&ret_delta_superblock);
  out:
+  g_clear_pointer (&target_uri, (GDestroyNotify) soup_uri_free);
   return ret;
 }
 
@@ -1734,47 +1748,9 @@ validate_variant_is_csum (GVariant       *csum,
   return ret;
 }
 
-/* documented in ostree-repo.c */
-gboolean
-ostree_repo_pull (OstreeRepo               *self,
-                  const char               *remote_name,
-                  char                    **refs_to_fetch,
-                  OstreeRepoPullFlags       flags,
-                  OstreeAsyncProgress      *progress,
-                  GCancellable             *cancellable,
-                  GError                  **error)
-{
-  return ostree_repo_pull_one_dir (self, remote_name, NULL, refs_to_fetch, flags, progress, cancellable, error);
-}
-
-/* Documented in ostree-repo.c */
-gboolean
-ostree_repo_pull_one_dir (OstreeRepo               *self,
-                          const char               *remote_name,
-                          const char               *dir_to_pull,
-                          char                    **refs_to_fetch,
-                          OstreeRepoPullFlags       flags,
-                          OstreeAsyncProgress      *progress,
-                          GCancellable             *cancellable,
-                          GError                  **error)
-{
-  GVariantBuilder builder;
-  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
-
-  if (dir_to_pull)
-    g_variant_builder_add (&builder, "{s@v}", "subdir",
-                           g_variant_new_variant (g_variant_new_string (dir_to_pull)));
-  g_variant_builder_add (&builder, "{s@v}", "flags",
-                         g_variant_new_variant (g_variant_new_int32 (flags)));
-  if (refs_to_fetch)
-    g_variant_builder_add (&builder, "{s@v}", "refs",
-                           g_variant_new_variant (g_variant_new_strv ((const char *const*) refs_to_fetch, -1)));
-
-  return ostree_repo_pull_with_options (self, remote_name, g_variant_builder_end (&builder),
-                                        progress, cancellable, error);
-}
-
-gboolean
+/* Load the summary from the cache if the provided .sig file is the same as the
+   cached version.  */
+static gboolean
 _ostree_repo_load_cache_summary_if_same_sig (OstreeRepo        *self,
                                              const char        *remote,
                                              GBytes            *summary_sig,
@@ -1836,7 +1812,7 @@ _ostree_repo_load_cache_summary_if_same_sig (OstreeRepo        *self,
   return ret;
 }
 
-gboolean
+static gboolean
 _ostree_repo_cache_summary (OstreeRepo        *self,
                             const char        *remote,
                             GBytes            *summary,
@@ -1876,7 +1852,311 @@ _ostree_repo_cache_summary (OstreeRepo        *self,
 
 }
 
-/* Documented in ostree-repo.c */
+static OstreeFetcher *
+_ostree_repo_remote_new_fetcher (OstreeRepo  *self,
+                                 const char  *remote_name,
+                                 GError     **error)
+{
+  OstreeFetcher *fetcher = NULL;
+  OstreeFetcherConfigFlags fetcher_flags = 0;
+  gboolean tls_permissive = FALSE;
+  gboolean success = FALSE;
+
+  g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
+  g_return_val_if_fail (remote_name != NULL, NULL);
+
+  if (!ostree_repo_get_remote_boolean_option (self, remote_name,
+                                              "tls-permissive", FALSE,
+                                              &tls_permissive, error))
+    goto out;
+
+  if (tls_permissive)
+    fetcher_flags |= OSTREE_FETCHER_FLAGS_TLS_PERMISSIVE;
+
+  fetcher = _ostree_fetcher_new (self->tmp_dir_fd, fetcher_flags);
+
+  {
+    g_autofree char *tls_client_cert_path = NULL;
+    g_autofree char *tls_client_key_path = NULL;
+
+    if (!ostree_repo_get_remote_option (self, remote_name,
+                                        "tls-client-cert-path", NULL,
+                                        &tls_client_cert_path, error))
+      goto out;
+    if (!ostree_repo_get_remote_option (self, remote_name,
+                                        "tls-client-key-path", NULL,
+                                        &tls_client_key_path, error))
+      goto out;
+
+    if ((tls_client_cert_path != NULL) != (tls_client_key_path != NULL))
+      {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                     "Remote \"%s\" must specify both "
+                     "\"tls-client-cert-path\" and \"tls-client-key-path\"",
+                     remote_name);
+        goto out;
+      }
+    else if (tls_client_cert_path != NULL)
+      {
+        g_autoptr(GTlsCertificate) client_cert = NULL;
+
+        g_assert (tls_client_key_path != NULL);
+
+        client_cert = g_tls_certificate_new_from_files (tls_client_cert_path,
+                                                        tls_client_key_path,
+                                                        error);
+        if (client_cert == NULL)
+          goto out;
+
+        _ostree_fetcher_set_client_cert (fetcher, client_cert);
+      }
+  }
+
+  {
+    g_autofree char *tls_ca_path = NULL;
+
+    if (!ostree_repo_get_remote_option (self, remote_name,
+                                        "tls-ca-path", NULL,
+                                        &tls_ca_path, error))
+      goto out;
+
+    if (tls_ca_path != NULL)
+      {
+        g_autoptr(GTlsDatabase) db = NULL;
+
+        db = g_tls_file_database_new (tls_ca_path, error);
+        if (db == NULL)
+          goto out;
+
+        _ostree_fetcher_set_tls_database (fetcher, db);
+      }
+  }
+
+  {
+    g_autofree char *http_proxy = NULL;
+
+    if (!ostree_repo_get_remote_option (self, remote_name,
+                                        "proxy", NULL,
+                                        &http_proxy, error))
+      goto out;
+
+    if (http_proxy != NULL)
+      _ostree_fetcher_set_proxy (fetcher, http_proxy);
+  }
+
+  success = TRUE;
+
+out:
+  if (!success)
+    g_clear_object (&fetcher);
+
+  return fetcher;
+}
+
+static gboolean
+_ostree_preload_metadata_file (OstreeRepo    *self,
+                               OstreeFetcher *fetcher,
+                               SoupURI       *base_uri,
+                               const char    *filename,
+                               gboolean      is_metalink,
+                               GBytes        **out_bytes,
+                               GCancellable  *cancellable,
+                               GError        **error)
+{
+  gboolean ret = FALSE;
+
+  if (is_metalink)
+    {
+      glnx_unref_object OstreeMetalink *metalink = NULL;
+      GError *local_error = NULL;
+
+      metalink = _ostree_metalink_new (fetcher, filename,
+                                       OSTREE_MAX_METADATA_SIZE,
+                                       base_uri);
+
+      _ostree_metalink_request_sync (metalink, NULL, out_bytes, NULL,
+                                     cancellable, &local_error);
+
+      if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+        {
+          g_clear_error (&local_error);
+          *out_bytes = NULL;
+        }
+      else if (local_error != NULL)
+        {
+          g_propagate_error (error, local_error);
+          goto out;
+        }
+    }
+  else
+    {
+      SoupURI *uri;
+      const char *base_path;
+      g_autofree char *path = NULL;
+
+      base_path = soup_uri_get_path (base_uri);
+      path = g_build_filename (base_path, filename, NULL);
+      uri = soup_uri_new_with_base (base_uri, path);
+
+      ret = _ostree_fetcher_request_uri_to_membuf (fetcher, uri,
+                                                   FALSE, TRUE,
+                                                   out_bytes,
+                                                   OSTREE_MAX_METADATA_SIZE,
+                                                   cancellable, error);
+      soup_uri_free (uri);
+
+      if (!ret)
+        goto out;
+    }
+
+  ret = TRUE;
+out:
+  return ret;
+}
+
+static gboolean
+repo_remote_fetch_summary (OstreeRepo    *self,
+                           const char    *name,
+                           const char    *metalink_url_string,
+                           GVariant      *options,
+                           GBytes       **out_summary,
+                           GBytes       **out_signatures,
+                           GCancellable  *cancellable,
+                           GError       **error)
+{
+  glnx_unref_object OstreeFetcher *fetcher = NULL;
+  g_autoptr(GMainContext) mainctx = NULL;
+  gboolean ret = FALSE;
+  SoupURI *base_uri = NULL;
+  gboolean from_cache = FALSE;
+  g_autofree char *url_override = NULL;
+
+  if (options)
+    (void) g_variant_lookup (options, "override-url", "&s", &url_override);
+
+  mainctx = g_main_context_new ();
+  g_main_context_push_thread_default (mainctx);
+
+  fetcher = _ostree_repo_remote_new_fetcher (self, name, error);
+  if (fetcher == NULL)
+    goto out;
+
+  {
+    g_autofree char *url_string = NULL;
+    if (metalink_url_string)
+      url_string = g_strdup (metalink_url_string);
+    else if (url_override)
+      url_string = g_strdup (url_override);
+    else if (!ostree_repo_remote_get_url (self, name, &url_string, error))
+      goto out;
+
+    base_uri = soup_uri_new (url_string);
+    if (base_uri == NULL)
+      {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                     "Invalid URL '%s'", url_string);
+        goto out;
+      }
+  }
+
+  if (!_ostree_preload_metadata_file (self,
+                                      fetcher,
+                                      base_uri,
+                                      "summary.sig",
+                                      metalink_url_string ? TRUE : FALSE,
+                                      out_signatures,
+                                      cancellable,
+                                      error))
+    goto out;
+
+  if (*out_signatures)
+    {
+      if (!_ostree_repo_load_cache_summary_if_same_sig (self,
+                                                        name,
+                                                        *out_signatures,
+                                                        out_summary,
+                                                        cancellable,
+                                                        error))
+        goto out;
+    }
+
+  if (*out_summary)
+    from_cache = TRUE;
+  else
+    {
+      if (!_ostree_preload_metadata_file (self,
+                                          fetcher,
+                                          base_uri,
+                                          "summary",
+                                          metalink_url_string ? TRUE : FALSE,
+                                          out_summary,
+                                          cancellable,
+                                          error))
+        goto out;
+    }
+
+  if (!from_cache && *out_summary && *out_signatures)
+    {
+      g_autoptr(GError) temp_error = NULL;
+
+      if (!_ostree_repo_cache_summary (self,
+                                       name,
+                                       *out_summary,
+                                       *out_signatures,
+                                       cancellable,
+                                       &temp_error))
+        {
+          if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
+            g_debug ("No permissions to save summary cache");
+          else
+            {
+              g_propagate_error (error, g_steal_pointer (&temp_error));
+              goto out;
+            }
+        }
+    }
+
+  ret = TRUE;
+
+ out:
+  if (mainctx)
+    g_main_context_pop_thread_default (mainctx);
+  if (base_uri != NULL)
+    soup_uri_free (base_uri);
+  return ret;
+}
+
+/* ------------------------------------------------------------------------------------------
+ * Below is the libsoup-invariant API; these should match
+ * the stub functions in the #else clause
+ * ------------------------------------------------------------------------------------------
+ */
+
+/**
+ * ostree_repo_pull_with_options:
+ * @self: Repo
+ * @remote_name: Name of remote
+ * @options: A GVariant a{sv} with an extensible set of flags.
+ * @progress: (allow-none): Progress
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Like ostree_repo_pull(), but supports an extensible set of flags.
+ * The following are currently defined:
+ *
+ *   * refs (as): Array of string refs
+ *   * flags (i): An instance of #OstreeRepoPullFlags
+ *   * subdir (s): Pull just this subdirectory
+ *   * override-remote-name (s): If local, add this remote to refspec
+ *   * gpg-verify (b): GPG verify commits
+ *   * gpg-verify-summary (b): GPG verify summary
+ *   * depth (i): How far in the history to traverse; default is 0, -1 means infinite
+ *   * disable-static-deltas (b): Do not use static deltas
+ *   * require-static-deltas (b): Require static deltas
+ *   * override-commit-ids (as): Array of specific commit IDs to fetch for refs
+ *   * dry-run (b): Only print information on what will be downloaded (requires static deltas)
+ *   * override-url (s): Fetch objects from this URL if remote specifies no metalink in options
+ */
 gboolean
 ostree_repo_pull_with_options (OstreeRepo             *self,
                                const char             *remote_name_or_baseurl,
@@ -1889,8 +2169,6 @@ ostree_repo_pull_with_options (OstreeRepo             *self,
   GHashTableIter hash_iter;
   gpointer key, value;
   g_autoptr(GBytes) bytes_summary = NULL;
-  g_autofree char *remote_key = NULL;
-  g_autofree char *path = NULL;
   g_autofree char *metalink_url_str = NULL;
   g_autoptr(GHashTable) requested_refs_to_fetch = NULL;
   g_autoptr(GHashTable) commits_to_fetch = NULL;
@@ -1904,13 +2182,14 @@ ostree_repo_pull_with_options (OstreeRepo             *self,
   guint64 end_time;
   OstreeRepoPullFlags flags = 0;
   const char *dir_to_pull = NULL;
-  char **refs_to_fetch = NULL;
+  g_autofree char **refs_to_fetch = NULL;
   char **override_commit_ids = NULL;
   GSource *update_timeout = NULL;
   gboolean disable_static_deltas = FALSE;
   gboolean require_static_deltas = FALSE;
   gboolean opt_gpg_verify = FALSE;
   gboolean opt_gpg_verify_summary = FALSE;
+  const char *url_override = NULL;
 
   if (options)
     {
@@ -1928,6 +2207,7 @@ ostree_repo_pull_with_options (OstreeRepo             *self,
       (void) g_variant_lookup (options, "require-static-deltas", "b", &require_static_deltas);
       (void) g_variant_lookup (options, "override-commit-ids", "^a&s", &override_commit_ids);
       (void) g_variant_lookup (options, "dry-run", "b", &pull_data->dry_run);
+      (void) g_variant_lookup (options, "override-url", "&s", &url_override);
     }
 
   g_return_val_if_fail (pull_data->maxdepth >= -1, FALSE);
@@ -2023,7 +2303,9 @@ ostree_repo_pull_with_options (OstreeRepo             *self,
     {
       g_autofree char *baseurl = NULL;
 
-      if (!ostree_repo_remote_get_url (self, remote_name_or_baseurl, &baseurl, error))
+      if (url_override != NULL)
+        baseurl = g_strdup (url_override);
+      else if (!ostree_repo_remote_get_url (self, remote_name_or_baseurl, &baseurl, error))
         goto out;
 
       pull_data->base_uri = soup_uri_new (baseurl);
@@ -2587,3 +2869,121 @@ ostree_repo_pull_with_options (OstreeRepo             *self,
   g_clear_pointer (&remote_config, (GDestroyNotify) g_key_file_unref);
   return ret;
 }
+
+/**
+ * ostree_repo_remote_fetch_summary_with_options:
+ * @self: Self
+ * @name: name of a remote
+ * @options: (nullable): A GVariant a{sv} with an extensible set of flags
+ * @out_summary: (nullable): return location for raw summary data, or %NULL
+ * @out_signatures: (nullable): return location for raw summary signature
+ *                              data, or %NULL
+ * @cancellable: a #GCancellable
+ * @error: a #GError
+ *
+ * Like ostree_repo_remote_fetch_summary(), but supports an extensible set of flags.
+ * The following are currently defined:
+ *
+ * - override-url (s): Fetch summary from this URL if remote specifies no metalink in options
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ */
+gboolean
+ostree_repo_remote_fetch_summary_with_options (OstreeRepo    *self,
+                                               const char    *name,
+                                               GVariant      *options,
+                                               GBytes       **out_summary,
+                                               GBytes       **out_signatures,
+                                               GCancellable  *cancellable,
+                                               GError       **error)
+{
+  g_autofree char *metalink_url_string = NULL;
+  g_autoptr(GBytes) summary = NULL;
+  g_autoptr(GBytes) signatures = NULL;
+  gboolean ret = FALSE;
+  gboolean gpg_verify_summary;
+
+  g_return_val_if_fail (OSTREE_REPO (self), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  if (!ostree_repo_get_remote_option (self, name, "metalink", NULL,
+                                      &metalink_url_string, error))
+    goto out;
+
+  if (!repo_remote_fetch_summary (self,
+                                  name,
+                                  metalink_url_string,
+                                  options,
+                                  &summary,
+                                  &signatures,
+                                  cancellable,
+                                  error))
+    goto out;
+
+  if (!ostree_repo_remote_get_gpg_verify_summary (self, name, &gpg_verify_summary, error))
+    goto out;
+
+  if (gpg_verify_summary && signatures == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "GPG verification enabled, but no summary signatures found (use gpg-verify-summary=false in remote config to disable)");
+      goto out;
+    }
+
+  /* Verify any summary signatures. */
+  if (gpg_verify_summary && summary != NULL && signatures != NULL)
+    {
+      glnx_unref_object OstreeGpgVerifyResult *result = NULL;
+
+      result = ostree_repo_verify_summary (self,
+                                           name,
+                                           summary,
+                                           signatures,
+                                           cancellable,
+                                           error);
+      if (!ostree_gpg_verify_result_require_valid_signature (result, error))
+        goto out;
+    }
+
+  if (out_summary != NULL)
+    *out_summary = g_steal_pointer (&summary);
+
+  if (out_signatures != NULL)
+    *out_signatures = g_steal_pointer (&signatures);
+
+  ret = TRUE;
+
+out:
+  return ret;
+}
+
+#else /* HAVE_LIBSOUP */
+
+gboolean
+ostree_repo_pull_with_options (OstreeRepo             *self,
+                               const char             *remote_name,
+                               GVariant               *options,
+                               OstreeAsyncProgress    *progress,
+                               GCancellable           *cancellable,
+                               GError                **error)
+{
+  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                       "This version of ostree was built without libsoup, and cannot fetch over HTTP");
+  return FALSE;
+}
+
+gboolean
+ostree_repo_remote_fetch_summary_with_options (OstreeRepo    *self,
+                                               const char    *name,
+                                               GVariant      *options,
+                                               GBytes       **out_summary,
+                                               GBytes       **out_signatures,
+                                               GCancellable  *cancellable,
+                                               GError       **error)
+{
+  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                       "This version of ostree was built without libsoup, and cannot fetch over HTTP");
+  return FALSE;
+}
+
+#endif /* HAVE_LIBSOUP */
index b398923473ab02d9b94811ecd769c34cc6ddc53f..4e4e7f9c4e8d4884ab929f279755aadef557f339 100644 (file)
@@ -1226,7 +1226,7 @@ get_fallback_headers (OstreeRepo               *self,
  *
  * The @params argument should be an a{sv}.  The following attributes
  * are known:
- *   - min-fallback-size: u: Minimume uncompressed size in megabytes to use fallback, 0 to disable fallbacks
+ *   - min-fallback-size: u: Minimum uncompressed size in megabytes to use fallback, 0 to disable fallbacks
  *   - max-chunk-size: u: Maximum size in megabytes of a delta part
  *   - max-bsdiff-size: u: Maximum size in megabytes to consider bsdiff compression
  *   for input files
@@ -1253,7 +1253,7 @@ ostree_repo_static_delta_generate (OstreeRepo                   *self,
   guint min_fallback_size;
   guint max_bsdiff_size;
   guint max_chunk_size;
-  GVariantBuilder metadata_builder;
+  g_auto(GVariantBuilder) metadata_builder = {{0,}};
   DeltaOpts delta_opts = DELTAOPT_FLAG_NONE;
   guint64 total_compressed_size = 0;
   guint64 total_uncompressed_size = 0;
@@ -1384,16 +1384,18 @@ ostree_repo_static_delta_generate (OstreeRepo                   *self,
       g_autoptr(GVariant) delta_part_content = NULL;
       g_autoptr(GVariant) delta_part = NULL;
       g_autoptr(GVariant) delta_part_header = NULL;
-      GVariantBuilder *mode_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(uuu)"));
-      GVariantBuilder *xattr_builder = g_variant_builder_new (G_VARIANT_TYPE ("aa(ayay)"));
+      g_auto(GVariantBuilder) mode_builder = {{0,}};
+      g_auto(GVariantBuilder) xattr_builder = {{0,}};
       guint8 compression_type_char;
 
+      g_variant_builder_init (&mode_builder, G_VARIANT_TYPE ("a(uuu)"));
+      g_variant_builder_init (&xattr_builder, G_VARIANT_TYPE ("aa(ayay)"));
       { guint j;
         for (j = 0; j < part_builder->modes->len; j++)
-          g_variant_builder_add_value (mode_builder, part_builder->modes->pdata[j]);
+          g_variant_builder_add_value (&mode_builder, part_builder->modes->pdata[j]);
         
         for (j = 0; j < part_builder->xattrs->len; j++)
-          g_variant_builder_add_value (xattr_builder, part_builder->xattrs->pdata[j]);
+          g_variant_builder_add_value (&xattr_builder, part_builder->xattrs->pdata[j]);
       }
         
       payload_b = g_string_free_to_bytes (part_builder->payload);
@@ -1403,7 +1405,7 @@ ostree_repo_static_delta_generate (OstreeRepo                   *self,
       part_builder->operations = NULL;
       /* FIXME - avoid duplicating memory here */
       delta_part_content = g_variant_new ("(a(uuu)aa(ayay)@ay@ay)",
-                                          mode_builder, xattr_builder,
+                                          &mode_builder, &xattr_builder,
                                           ot_gvariant_new_ay_bytes (payload_b),
                                           ot_gvariant_new_ay_bytes (operations_b));
       g_variant_ref_sink (delta_part_content);
@@ -1481,7 +1483,8 @@ ostree_repo_static_delta_generate (OstreeRepo                   *self,
 
   descriptor_dir = g_file_get_parent (descriptor_path);
 
-  if (!gs_file_ensure_directory (descriptor_dir, TRUE, cancellable, error))
+  if (!glnx_shutil_mkdir_p_at (AT_FDCWD, gs_file_get_path_cached (descriptor_dir), 0755,
+                               cancellable, error))
     goto out;
 
   for (i = 0; i < part_tempfiles->len; i++)
index 9e3ed09f65e79934751bd879ed543209b2c68137..de9e6c7a94c15e12c17c170c4d7f2ffdeef40e9c 100644 (file)
@@ -126,8 +126,8 @@ ostree_repo_list_static_delta_names (OstreeRepo                  *self,
               if (g_file_info_get_file_type (file_info2) != G_FILE_TYPE_DIRECTORY)
                 continue;
 
-              name1 = gs_file_get_basename_cached (child);
-              name2 = gs_file_get_basename_cached (child2);
+              name1 = g_file_info_get_name (file_info);
+              name2 = g_file_info_get_name (file_info2);
 
               {
                 g_autoptr(GFile) meta_path = g_file_get_child (child2, "superblock");
@@ -159,7 +159,8 @@ ostree_repo_list_static_delta_names (OstreeRepo                  *self,
     }
 
   ret = TRUE;
-  gs_transfer_out_value (out_deltas, &ret_deltas);
+  if (out_deltas)
+    *out_deltas = g_steal_pointer (&ret_deltas);
  out:
   return ret;
 }
@@ -512,6 +513,7 @@ _ostree_static_delta_part_open (GInputStream   *part_in,
                                                                     g_bytes_get_size (inline_part_bytes) - 1);
           ret_part = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                                content_bytes, trusted);
+          g_variant_ref_sink (ret_part);
         }
 
       if (!skip_checksum)
@@ -840,7 +842,7 @@ _ostree_repo_static_delta_dump (OstreeRepo                    *self,
 
   if (!ot_util_variant_map_at (self->repo_dir_fd, superblock_path,
                                (GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
-                               TRUE, &delta_superblock, error))
+                               OT_VARIANT_MAP_TRUSTED, &delta_superblock, error))
     goto out;
 
   g_print ("%s\n", g_variant_print (delta_superblock, 1));
index bb437c38b8fcb4eda47761a7521d188a8f787a89..503ab32972565908f2382b8c031030022337cb6f 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "config.h"
 
+#include "libglnx.h"
 #include "ostree.h"
 #include "otutil.h"
 
index 08e6a48fa821b5d94a9ded0eaee8cd4ebbec5e12..e86685bf5f3e5ad3588b9a524b82df145abae9dc 100644 (file)
@@ -26,7 +26,9 @@
 #include <glib-unix.h>
 #include <gio/gunixinputstream.h>
 #include <gio/gfiledescriptorbased.h>
+#include "libglnx.h"
 #include "otutil.h"
+#include <glnx-console.h>
 
 #include "ostree-core-private.h"
 #include "ostree-repo-private.h"
 #include "ostree-gpg-verifier.h"
 #include "ostree-repo-static-delta-private.h"
 #include "ot-fs-utils.h"
-
-#ifdef HAVE_LIBSOUP
-#include "ostree-metalink.h"
-#endif
+#include "ostree-autocleanups.h"
 
 #include <locale.h>
 #include <glib/gstdio.h>
 
 /**
- * SECTION:libostree-repo
+ * SECTION:ostree-repo
  * @title: Content-addressed object store
  * @short_description: A git-like storage system for operating system binaries
  *
@@ -131,7 +130,7 @@ static OstreeRemote *
 ost_remote_new_from_keyfile (GKeyFile    *keyfile,
                              const gchar *group)
 {
-  GMatchInfo *match = NULL;
+  g_autoptr(GMatchInfo) match = NULL;
   OstreeRemote *remote;
 
   static gsize regex_initialized;
@@ -158,8 +157,6 @@ ost_remote_new_from_keyfile (GKeyFile    *keyfile,
 
   ot_keyfile_copy_group (keyfile, remote->options, group);
 
-  g_match_info_unref (match);
-
   return remote;
 }
 
@@ -322,12 +319,16 @@ ostree_repo_get_remote_option (OstreeRepo  *self,
         {
           if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
             {
-              if (self->parent_repo != NULL)
-                return ostree_repo_get_remote_option (self->parent_repo,
-                                                      remote_name, option_name,
-                                                      default_value,
-                                                      out_value,
-                                                      error);
+              /* Note: We ignore errors on the parent because the parent config may not
+                 specify this remote, causing a "remote not found" error, but we found
+                 the remote at some point, so we need to instead return the default */
+              if (self->parent_repo != NULL &&
+                  ostree_repo_get_remote_option (self->parent_repo,
+                                                 remote_name, option_name,
+                                                 default_value,
+                                                 out_value,
+                                                 NULL))
+                return TRUE;
 
               value = g_strdup (default_value);
               ret = TRUE;
@@ -397,11 +398,16 @@ ostree_repo_get_remote_list_option (OstreeRepo   *self,
       /* Default value if key not found is always NULL. */
       if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
         {
-          if (self->parent_repo != NULL)
-            return ostree_repo_get_remote_list_option (self->parent_repo,
-                                                       remote_name, option_name,
-                                                       out_value,
-                                                       error);
+          /* Note: We ignore errors on the parent because the parent config may not
+             specify this remote, causing a "remote not found" error, but we found
+             the remote at some point, so we need to instead return the default */
+          if (self->parent_repo != NULL &&
+              ostree_repo_get_remote_list_option (self->parent_repo,
+                                                  remote_name, option_name,
+                                                  out_value,
+                                                  NULL))
+            return TRUE;
+
           ret = TRUE;
         }
       else if (temp_error)
@@ -426,7 +432,7 @@ ostree_repo_get_remote_list_option (OstreeRepo   *self,
  * @self: A OstreeRepo
  * @remote_name: Name
  * @option_name: Option
- * @default_value: (allow-none): Value returned if @option_name is not present
+ * @default_value: Value returned if @option_name is not present
  * @out_value: (out) : location to store the result.
  * @error: Error
  *
@@ -464,12 +470,16 @@ ostree_repo_get_remote_boolean_option (OstreeRepo  *self,
         {
           if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
             {
-              if (self->parent_repo != NULL)
-                return ostree_repo_get_remote_boolean_option (self->parent_repo,
-                                                              remote_name, option_name,
-                                                              default_value,
-                                                              out_value,
-                                                              error);
+              /* Note: We ignore errors on the parent because the parent config may not
+                 specify this remote, causing a "remote not found" error, but we found
+                 the remote at some point, so we need to instead return the default */
+              if (self->parent_repo != NULL &&
+                  ostree_repo_get_remote_boolean_option (self->parent_repo,
+                                                         remote_name, option_name,
+                                                         default_value,
+                                                         out_value,
+                                                         NULL))
+                return TRUE;
 
               value = default_value;
               ret = TRUE;
@@ -493,109 +503,6 @@ ostree_repo_get_remote_boolean_option (OstreeRepo  *self,
   return ret;
 }
 
-#ifdef HAVE_LIBSOUP
-OstreeFetcher *
-_ostree_repo_remote_new_fetcher (OstreeRepo  *self,
-                                 const char  *remote_name,
-                                 GError     **error)
-{
-  OstreeFetcher *fetcher = NULL;
-  OstreeFetcherConfigFlags fetcher_flags = 0;
-  gboolean tls_permissive = FALSE;
-  gboolean success = FALSE;
-
-  g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
-  g_return_val_if_fail (remote_name != NULL, NULL);
-
-  if (!ostree_repo_get_remote_boolean_option (self, remote_name,
-                                              "tls-permissive", FALSE,
-                                              &tls_permissive, error))
-    goto out;
-
-  if (tls_permissive)
-    fetcher_flags |= OSTREE_FETCHER_FLAGS_TLS_PERMISSIVE;
-
-  fetcher = _ostree_fetcher_new (self->tmp_dir_fd, fetcher_flags);
-
-  {
-    g_autofree char *tls_client_cert_path = NULL;
-    g_autofree char *tls_client_key_path = NULL;
-
-    if (!ostree_repo_get_remote_option (self, remote_name,
-                                        "tls-client-cert-path", NULL,
-                                        &tls_client_cert_path, error))
-      goto out;
-    if (!ostree_repo_get_remote_option (self, remote_name,
-                                        "tls-client-key-path", NULL,
-                                        &tls_client_key_path, error))
-      goto out;
-
-    if ((tls_client_cert_path != NULL) != (tls_client_key_path != NULL))
-      {
-        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                     "Remote \"%s\" must specify both "
-                     "\"tls-client-cert-path\" and \"tls-client-key-path\"",
-                     remote_name);
-        goto out;
-      }
-    else if (tls_client_cert_path != NULL)
-      {
-        g_autoptr(GTlsCertificate) client_cert = NULL;
-
-        g_assert (tls_client_key_path != NULL);
-
-        client_cert = g_tls_certificate_new_from_files (tls_client_cert_path,
-                                                        tls_client_key_path,
-                                                        error);
-        if (client_cert == NULL)
-          goto out;
-
-        _ostree_fetcher_set_client_cert (fetcher, client_cert);
-      }
-  }
-
-  {
-    g_autofree char *tls_ca_path = NULL;
-
-    if (!ostree_repo_get_remote_option (self, remote_name,
-                                        "tls-ca-path", NULL,
-                                        &tls_ca_path, error))
-      goto out;
-
-    if (tls_ca_path != NULL)
-      {
-        g_autoptr(GTlsDatabase) db = NULL;
-
-        db = g_tls_file_database_new (tls_ca_path, error);
-        if (db == NULL)
-          goto out;
-
-        _ostree_fetcher_set_tls_database (fetcher, db);
-      }
-  }
-
-  {
-    g_autofree char *http_proxy = NULL;
-
-    if (!ostree_repo_get_remote_option (self, remote_name,
-                                        "proxy", NULL,
-                                        &http_proxy, error))
-      goto out;
-
-    if (http_proxy != NULL)
-      _ostree_fetcher_set_proxy (fetcher, http_proxy);
-  }
-
-  success = TRUE;
-
-out:
-  if (!success)
-    g_clear_object (&fetcher);
-
-  return fetcher;
-}
-#endif
-
 static void
 ostree_repo_finalize (GObject *object)
 {
@@ -603,7 +510,7 @@ ostree_repo_finalize (GObject *object)
 
   g_clear_object (&self->parent_repo);
 
-  g_free (self->boot_id);
+  g_free (self->stagedir_prefix);
   g_clear_object (&self->repodir);
   if (self->repo_dir_fd != -1)
     (void) close (self->repo_dir_fd);
@@ -784,6 +691,9 @@ ostree_repo_init (OstreeRepo *self)
 {
   static gsize gpgme_initialized;
   GLnxLockFile empty_lockfile = GLNX_LOCK_FILE_INIT;
+  const GDebugKey test_error_keys[] = {
+    { "pre-commit", OSTREE_REPO_TEST_ERROR_PRE_COMMIT },
+  };
 
   if (g_once_init_enter (&gpgme_initialized))
     {
@@ -792,6 +702,9 @@ ostree_repo_init (OstreeRepo *self)
       g_once_init_leave (&gpgme_initialized, 1);
     }
 
+  self->test_error_flags = g_parse_debug_string (g_getenv ("OSTREE_REPO_TEST_ERROR"),
+                                                 test_error_keys, G_N_ELEMENTS (test_error_keys));
+
   g_mutex_init (&self->cache_lock);
   g_mutex_init (&self->txn_stats_lock);
 
@@ -920,6 +833,14 @@ ostree_repo_is_writable (OstreeRepo *self,
   return self->writable;
 }
 
+/**
+ * _ostree_repo_update_mtime:
+ * @self: Repo
+ * @error: a #GError
+ *
+ * Bump the mtime of the repository so that programs
+ * can detect that the refs have updated.
+ */
 gboolean
 _ostree_repo_update_mtime (OstreeRepo        *self,
                            GError           **error)
@@ -1233,8 +1154,11 @@ impl_repo_remote_delete (OstreeRepo     *self,
 
   if (remote->file != NULL)
     {
-      if (!gs_file_unlink (remote->file, cancellable, error))
-        goto out;
+      if (unlink (gs_file_get_path_cached (remote->file)) != 0)
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
     }
   else
     {
@@ -1809,185 +1733,12 @@ out:
   return ret;
 }
 
-#ifdef HAVE_LIBSOUP
-static gboolean
-_ostree_preload_metadata_file (OstreeRepo    *self,
-                               OstreeFetcher *fetcher,
-                               SoupURI       *base_uri,
-                               const char    *filename,
-                               gboolean      is_metalink,
-                               GBytes        **out_bytes,
-                               GCancellable  *cancellable,
-                               GError        **error)
-{
-  gboolean ret = FALSE;
-
-  if (is_metalink)
-    {
-      glnx_unref_object OstreeMetalink *metalink = NULL;
-      GError *local_error = NULL;
-
-      metalink = _ostree_metalink_new (fetcher, filename,
-                                       OSTREE_MAX_METADATA_SIZE,
-                                       base_uri);
-
-      _ostree_metalink_request_sync (metalink, NULL, out_bytes, NULL,
-                                     cancellable, &local_error);
-
-      if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
-        {
-          g_clear_error (&local_error);
-          *out_bytes = NULL;
-        }
-      else if (local_error != NULL)
-        {
-          g_propagate_error (error, local_error);
-          goto out;
-        }
-    }
-  else
-    {
-      SoupURI *uri;
-      const char *base_path;
-      g_autofree char *path = NULL;
-
-      base_path = soup_uri_get_path (base_uri);
-      path = g_build_filename (base_path, filename, NULL);
-      uri = soup_uri_new_with_base (base_uri, path);
-
-      ret = _ostree_fetcher_request_uri_to_membuf (fetcher, uri,
-                                                   FALSE, TRUE,
-                                                   out_bytes,
-                                                   OSTREE_MAX_METADATA_SIZE,
-                                                   cancellable, error);
-      soup_uri_free (uri);
-
-      if (!ret)
-        goto out;
-    }
-
-  ret = TRUE;
-out:
-  return ret;
-}
-
-static gboolean
-repo_remote_fetch_summary (OstreeRepo    *self,
-                           const char    *name,
-                           const char    *metalink_url_string,
-                           GBytes       **out_summary,
-                           GBytes       **out_signatures,
-                           GCancellable  *cancellable,
-                           GError       **error)
-{
-  glnx_unref_object OstreeFetcher *fetcher = NULL;
-  g_autoptr(GMainContext) mainctx = NULL;
-  gboolean ret = FALSE;
-  SoupURI *base_uri = NULL;
-  gboolean from_cache = FALSE;
-
-  mainctx = g_main_context_new ();
-  g_main_context_push_thread_default (mainctx);
-
-  fetcher = _ostree_repo_remote_new_fetcher (self, name, error);
-  if (fetcher == NULL)
-    goto out;
-
-  base_uri = soup_uri_new (metalink_url_string);
-
-  {
-    g_autofree char *url_string = NULL;
-    if (metalink_url_string)
-      url_string = g_strdup (metalink_url_string);
-    else
-      {
-        if (!ostree_repo_remote_get_url (self, name, &url_string, error))
-          goto out;
-      }
-
-    base_uri = soup_uri_new (url_string);
-    if (base_uri == NULL)
-      {
-        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                     "Invalid URL '%s'", url_string);
-        goto out;
-      }
-  }
-
-  if (!_ostree_preload_metadata_file (self,
-                                      fetcher,
-                                      base_uri,
-                                      "summary.sig",
-                                      metalink_url_string ? TRUE : FALSE,
-                                      out_signatures,
-                                      cancellable,
-                                      error))
-    goto out;
-
-  if (*out_signatures)
-    {
-      if (!_ostree_repo_load_cache_summary_if_same_sig (self,
-                                                        name,
-                                                        *out_signatures,
-                                                        out_summary,
-                                                        cancellable,
-                                                        error))
-        goto out;
-    }
-
-  if (*out_summary)
-    from_cache = TRUE;
-  else
-    {
-      if (!_ostree_preload_metadata_file (self,
-                                          fetcher,
-                                          base_uri,
-                                          "summary",
-                                          metalink_url_string ? TRUE : FALSE,
-                                          out_summary,
-                                          cancellable,
-                                          error))
-        goto out;
-    }
-
-  if (!from_cache && *out_summary && *out_signatures)
-    {
-      g_autoptr(GError) temp_error = NULL;
-
-      if (!_ostree_repo_cache_summary (self,
-                                       name,
-                                       *out_summary,
-                                       *out_signatures,
-                                       cancellable,
-                                       &temp_error))
-        {
-          if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
-            g_debug ("No permissions to save summary cache");
-          else
-            {
-              g_propagate_error (error, g_steal_pointer (&temp_error));
-              goto out;
-            }
-        }
-    }
-
-  ret = TRUE;
-
- out:
-  if (mainctx)
-    g_main_context_pop_thread_default (mainctx);
-  if (base_uri != NULL)
-    soup_uri_free (base_uri);
-  return ret;
-}
-#endif
-
 /**
  * ostree_repo_remote_fetch_summary:
  * @self: Self
  * @name: name of a remote
- * @out_summary: (allow-none): return location for raw summary data, or %NULL
- * @out_signatures: (allow-none): return location for raw summary signature
+ * @out_summary: (nullable): return location for raw summary data, or %NULL
+ * @out_signatures: (nullable): return location for raw summary signature
  *                                data, or %NULL
  * @cancellable: a #GCancellable
  * @error: a #GError
@@ -2013,76 +1764,13 @@ ostree_repo_remote_fetch_summary (OstreeRepo    *self,
                                   GCancellable  *cancellable,
                                   GError       **error)
 {
-#ifdef HAVE_LIBSOUP
-  g_autofree char *metalink_url_string = NULL;
-  g_autoptr(GBytes) summary = NULL;
-  g_autoptr(GBytes) signatures = NULL;
-  gboolean ret = FALSE;
-  gboolean gpg_verify_summary;
-
-  g_return_val_if_fail (OSTREE_REPO (self), FALSE);
-  g_return_val_if_fail (name != NULL, FALSE);
-
-  if (!ostree_repo_get_remote_option (self, name, "metalink", NULL,
-                                      &metalink_url_string, error))
-    goto out;
-
-  if (!repo_remote_fetch_summary (self,
-                                  name,
-                                  metalink_url_string,
-                                  &summary,
-                                  &signatures,
-                                  cancellable,
-                                  error))
-    goto out;
-
-  if (!ostree_repo_remote_get_gpg_verify_summary (self, name, &gpg_verify_summary, error))
-    goto out;
-
-  if (gpg_verify_summary && signatures == NULL)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "GPG verification enabled, but no summary signatures found (use gpg-verify-summary=false in remote config to disable)");
-      goto out;
-    }
-
-  /* Verify any summary signatures. */
-  if (gpg_verify_summary && summary != NULL && signatures != NULL)
-    {
-      glnx_unref_object OstreeGpgVerifyResult *result = NULL;
-
-      result = ostree_repo_verify_summary (self,
-                                           name,
-                                           summary,
-                                           signatures,
-                                           cancellable,
-                                           error);
-      if (result == NULL)
-        goto out;
-
-      if (ostree_gpg_verify_result_count_valid (result) == 0)
-        {
-          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                               "GPG signatures found, but none are in trusted keyring");
-          goto out;
-        }
-    }
-
-  if (out_summary != NULL)
-    *out_summary = g_steal_pointer (&summary);
-
-  if (out_signatures != NULL)
-    *out_signatures = g_steal_pointer (&signatures);
-
-  ret = TRUE;
-
-out:
-  return ret;
-#else
-  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                       "This version of ostree was built without libsoup, and cannot fetch over HTTP");
-  return FALSE;
-#endif
+  return ostree_repo_remote_fetch_summary_with_options (self,
+                                                        name,
+                                                        NULL,
+                                                        out_summary,
+                                                        out_signatures,
+                                                        cancellable,
+                                                        error);
 }
 
 static gboolean
@@ -2128,7 +1816,8 @@ ostree_repo_mode_from_string (const char      *mode,
     ret_mode = OSTREE_REPO_MODE_BARE;
   else if (strcmp (mode, "bare-user") == 0)
     ret_mode = OSTREE_REPO_MODE_BARE_USER;
-  else if (strcmp (mode, "archive-z2") == 0)
+  else if (strcmp (mode, "archive-z2") == 0 ||
+           strcmp (mode, "archive") == 0)
     ret_mode = OSTREE_REPO_MODE_ARCHIVE_Z2;
   else
     {
@@ -2171,8 +1860,14 @@ ostree_repo_create (OstreeRepo     *self,
   if (!ostree_repo_mode_to_string (mode, &mode_str, error))
     goto out;
 
-  if (!gs_file_ensure_directory (self->repodir, FALSE, cancellable, error))
-    goto out;
+  if (mkdir (gs_file_get_path_cached (self->repodir), 0755) != 0)
+    {
+      if (errno != EEXIST)
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+    }
 
   config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
   g_string_append_printf (config_data, "mode=%s\n", mode_str);
@@ -2190,6 +1885,13 @@ ostree_repo_create (OstreeRepo     *self,
   if (!g_file_make_directory (self->tmp_dir, cancellable, error))
     goto out;
 
+  {
+    g_autoptr(GFile) extensions_dir =
+      g_file_resolve_relative_path (self->repodir, "extensions");
+    if (!g_file_make_directory (extensions_dir, cancellable, error))
+      goto out;
+  }
+
   g_clear_object (&child);
   child = g_file_get_child (self->repodir, "refs");
   if (!g_file_make_directory (child, cancellable, error))
@@ -2402,22 +2104,33 @@ ostree_repo_open (OstreeRepo    *self,
   g_autofree char *version = NULL;
   g_autofree char *mode = NULL;
   g_autofree char *parent_repo_path = NULL;
-  g_autoptr(GError) temp_error = NULL;
 
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
   if (self->inited)
     return TRUE;
 
-  /* We use a per-boot identifier to keep track of which file contents
-   * possibly haven't been sync'd to disk.
+  /* We use a directory of the form `staging-${BOOT_ID}-${RANDOM}`
+   * where if the ${BOOT_ID} doesn't match, we know file contents
+   * possibly haven't been sync'd to disk and need to be discarded.
    */
-  if (!g_file_get_contents ("/proc/sys/kernel/random/boot_id",
-                           &self->boot_id,
-                           NULL,
-                           error))
-    goto out;
-  g_strdelimit (self->boot_id, "\n", '\0');
+  { const char *env_bootid = getenv ("OSTREE_BOOTID");
+    g_autofree char *boot_id = NULL;
+
+    if (env_bootid != NULL)
+      boot_id = g_strdup (env_bootid);
+    else
+      {
+        if (!g_file_get_contents ("/proc/sys/kernel/random/boot_id",
+                                  &boot_id,
+                                  NULL,
+                                  error))
+          goto out;
+        g_strdelimit (boot_id, "\n", '\0');
+      }
+
+    self->stagedir_prefix = g_strconcat (OSTREE_REPO_TMPDIR_STAGING, boot_id, "-", NULL);
+  }
 
   if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (self->repodir), TRUE,
                        &self->repo_dir_fd, error))
@@ -2530,43 +2243,35 @@ ostree_repo_open (OstreeRepo    *self,
       ostree_repo_set_disable_fsync (self, TRUE);
   }
 
+  { g_autofree char *tmp_expiry_seconds = NULL;
+
+    /* 86400 secs = one day */
+    if (!ot_keyfile_get_value_with_default (self->config, "core", "tmp-expiry-secs", "86400",
+                                            &tmp_expiry_seconds, error))
+      goto out;
+
+    self->tmp_expiry_seconds = g_ascii_strtoull (tmp_expiry_seconds, NULL, 10);
+  }
+
   if (!append_remotes_d (self, cancellable, error))
     goto out;
 
   if (!glnx_opendirat (self->repo_dir_fd, "tmp", TRUE, &self->tmp_dir_fd, error))
     goto out;
 
-  if (!glnx_shutil_mkdir_p_at (self->tmp_dir_fd, _OSTREE_CACHE_DIR, 0775, cancellable, &temp_error))
+  if (self->writable)
     {
-      if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
-        {
-          g_clear_error (&temp_error);
-          g_debug ("No permissions to create cache dir");
-        }
-      else
-        {
-          g_propagate_error (error, g_steal_pointer (&temp_error));
-          goto out;
-        }
-    }
+      if (!glnx_shutil_mkdir_p_at (self->tmp_dir_fd, _OSTREE_CACHE_DIR, 0775, cancellable, error))
+        goto out;
 
-  if (!glnx_opendirat (self->tmp_dir_fd, _OSTREE_CACHE_DIR, TRUE, &self->cache_dir_fd, &temp_error))
-    {
-      if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
-        {
-          g_clear_error (&temp_error);
-          g_debug ("No cache dir");
-        }
-      else
-        {
-          g_propagate_error (error, g_steal_pointer (&temp_error));
-          goto out;
-        }
+      if (!glnx_opendirat (self->tmp_dir_fd, _OSTREE_CACHE_DIR, TRUE, &self->cache_dir_fd, error))
+        goto out;
     }
 
   if (self->mode == OSTREE_REPO_MODE_ARCHIVE_Z2 && self->enable_uncompressed_cache)
     {
-      if (!gs_file_ensure_directory (self->uncompressed_objects_dir, TRUE, cancellable, error))
+      if (!glnx_shutil_mkdir_p_at (self->repo_dir_fd, "uncompressed-objects-cache", 0755,
+                                   cancellable, error))
         goto out;
       if (!glnx_opendirat (self->repo_dir_fd, "uncompressed-objects-cache", TRUE,
                            &self->uncompressed_objects_dir_fd,
@@ -2603,6 +2308,8 @@ ostree_repo_set_disable_fsync (OstreeRepo    *self,
  * @self: An #OstreeRepo
  * @dfd: directory fd
  * @path: subpath in @dfd
+ * @cancellable: a #GCancellable
+ * @error: a #GError
  *
  * Set a custom location for the cache directory used for e.g.
  * per-remote summary caches. Setting this manually is useful when
@@ -2852,7 +2559,7 @@ load_metadata_internal (OstreeRepo       *self,
 {
   gboolean ret = FALSE;
   char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
-  int fd = -1;
+  glnx_fd_close int fd = -1;
   g_autoptr(GInputStream) ret_stream = NULL;
   g_autoptr(GVariant) ret_variant = NULL;
 
@@ -2880,8 +2587,6 @@ load_metadata_internal (OstreeRepo       *self,
           mfile = g_mapped_file_new_from_fd (fd, FALSE, error);
           if (!mfile)
             goto out;
-          (void) close (fd); /* Ignore errors, we have it mapped */
-          fd = -1;
           ret_variant = g_variant_new_from_data (ostree_metadata_variant_type (objtype),
                                                  g_mapped_file_get_contents (mfile),
                                                  g_mapped_file_get_length (mfile),
@@ -2903,7 +2608,7 @@ load_metadata_internal (OstreeRepo       *self,
             {
               struct stat stbuf;
 
-              if (!gs_stream_fstat ((GFileDescriptorBased*)ret_stream, &stbuf, cancellable, error))
+              if (!glnx_stream_fstat ((GFileDescriptorBased*)ret_stream, &stbuf, error))
                 goto out;
               *out_size = stbuf.st_size;
             }
@@ -2926,8 +2631,6 @@ load_metadata_internal (OstreeRepo       *self,
   ot_transfer_out_value (out_variant, &ret_variant);
   ot_transfer_out_value (out_stream, &ret_stream);
  out:
-  if (fd != -1)
-    (void) close (fd);
   return ret;
 }
 
@@ -3018,8 +2721,13 @@ _ostree_repo_read_bare_fd (OstreeRepo           *self,
 
   _ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, self->mode);
   
-  return gs_file_openat_noatime (self->objects_dir_fd, loose_path_buf, out_fd,
-                               cancellable, error);
+  *out_fd = openat (self->objects_dir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
+  if (*out_fd < 0)
+    {
+      glnx_set_error_from_errno (error);
+      return FALSE;
+    }
+  return TRUE;
 }
 
 /**
@@ -3066,13 +2774,20 @@ ostree_repo_load_file (OstreeRepo         *self,
                                     error))
         goto out;
 
+      if (fd < 0 && self->commit_stagedir_fd != -1)
+        {
+          if (!ot_openat_ignore_enoent (self->commit_stagedir_fd, loose_path_buf, &fd,
+                                        error))
+            goto out;
+        }
+
       if (fd != -1)
         {
           tmp_stream = g_unix_input_stream_new (fd, TRUE);
           fd = -1; /* Transfer ownership */
           
-          if (!gs_stream_fstat ((GFileDescriptorBased*) tmp_stream, &stbuf,
-                                cancellable, error))
+          if (!glnx_stream_fstat ((GFileDescriptorBased*) tmp_stream, &stbuf,
+                                  error))
             goto out;
           
           if (!ostree_content_stream_parse (TRUE, tmp_stream, stbuf.st_size, TRUE,
@@ -3285,15 +3000,13 @@ _ostree_repo_has_loose_object (OstreeRepo           *self,
                                const char           *checksum,
                                OstreeObjectType      objtype,
                                gboolean             *out_is_stored,
-                               char                 *loose_path_buf,
-                               GFile               **out_stored_path,
                                GCancellable         *cancellable,
                                GError             **error)
 {
   gboolean ret = FALSE;
   struct stat stbuf;
   int res = -1;
-  gboolean tmp_file = FALSE;
+  char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
 
   _ostree_loose_path (loose_path_buf, checksum, objtype, self->mode);
 
@@ -3309,9 +3022,7 @@ _ostree_repo_has_loose_object (OstreeRepo           *self,
         }
     }
 
-  if (res == 0)
-    tmp_file = TRUE;
-  else
+  if (res < 0)
     {
       do
         res = fstatat (self->objects_dir_fd, loose_path_buf, &stbuf, AT_SYMLINK_NOFOLLOW);
@@ -3325,32 +3036,10 @@ _ostree_repo_has_loose_object (OstreeRepo           *self,
 
   ret = TRUE;
   *out_is_stored = (res != -1);
-
-  if (out_stored_path)
-    {
-      if (res != -1)
-        *out_stored_path = g_file_resolve_relative_path (tmp_file ? self->tmp_dir : self->objects_dir, loose_path_buf);
-      else
-        *out_stored_path = NULL;
-    }
 out:
   return ret;
 }
 
-gboolean
-_ostree_repo_find_object (OstreeRepo           *self,
-                          OstreeObjectType      objtype,
-                          const char           *checksum,
-                          GFile               **out_stored_path,
-                          GCancellable         *cancellable,
-                          GError             **error)
-{
-  gboolean has_object;
-  char loose_path[_OSTREE_LOOSE_PATH_MAX];
-  return _ostree_repo_has_loose_object (self, checksum, objtype, &has_object, loose_path,
-                                        out_stored_path, cancellable, error);
-}
-
 /**
  * ostree_repo_has_object:
  * @self: Repo
@@ -3375,13 +3064,12 @@ ostree_repo_has_object (OstreeRepo           *self,
 {
   gboolean ret = FALSE;
   gboolean ret_have_object;
-  g_autoptr(GFile) loose_path = NULL;
 
-  if (!_ostree_repo_find_object (self, objtype, checksum, &loose_path,
-                                 cancellable, error))
+  if (!_ostree_repo_has_loose_object (self, checksum, objtype, &ret_have_object,
+                                      cancellable, error))
     goto out;
 
-  ret_have_object = (loose_path != NULL);
+  /* In the future, here is where we would also look up in metadata pack files */
 
   if (!ret_have_object && self->parent_repo)
     {
@@ -3462,13 +3150,16 @@ ostree_repo_delete_object (OstreeRepo           *self,
 
       if (tombstone_commits)
         {
-          g_autoptr(GVariantBuilder) builder = NULL;
-          builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
-          g_variant_builder_add (builder, "{sv}", "commit", g_variant_new_bytestring (sha256));
+          g_auto(GVariantBuilder) builder = {{0,}};
+          g_autoptr(GVariant) variant = NULL;
+
+          g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+          g_variant_builder_add (&builder, "{sv}", "commit", g_variant_new_bytestring (sha256));
+          variant = g_variant_ref_sink (g_variant_builder_end (&builder));
           if (!ostree_repo_write_metadata_trusted (self,
                                                    OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT,
                                                    sha256,
-                                                   g_variant_builder_end (builder),
+                                                   variant,
                                                    cancellable,
                                                    error))
             goto out;
@@ -4003,7 +3694,6 @@ ostree_repo_read_commit (OstreeRepo   *self,
   return ret;
 }
 
-#ifndef HAVE_LIBSOUP
 /**
  * ostree_repo_pull:
  * @self: Repo
@@ -4040,9 +3730,7 @@ ostree_repo_pull (OstreeRepo               *self,
                   GCancellable             *cancellable,
                   GError                  **error)
 {
-  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                       "This version of ostree was built without libsoup, and cannot fetch over HTTP");
-  return FALSE;
+  return ostree_repo_pull_one_dir (self, remote_name, NULL, refs_to_fetch, flags, progress, cancellable, error);
 }
 
 /**
@@ -4069,49 +3757,55 @@ ostree_repo_pull_one_dir (OstreeRepo               *self,
                           GCancellable             *cancellable,
                           GError                  **error)
 {
-  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                       "This version of ostree was built without libsoup, and cannot fetch over HTTP");
-  return FALSE;
+  GVariantBuilder builder;
+  g_autoptr(GVariant) options = NULL;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+  if (dir_to_pull)
+    g_variant_builder_add (&builder, "{s@v}", "subdir",
+                           g_variant_new_variant (g_variant_new_string (dir_to_pull)));
+  g_variant_builder_add (&builder, "{s@v}", "flags",
+                         g_variant_new_variant (g_variant_new_int32 (flags)));
+  if (refs_to_fetch)
+    g_variant_builder_add (&builder, "{s@v}", "refs",
+                           g_variant_new_variant (g_variant_new_strv ((const char *const*) refs_to_fetch, -1)));
+
+  options = g_variant_ref_sink (g_variant_builder_end (&builder));
+  return ostree_repo_pull_with_options (self, remote_name, options,
+                                        progress, cancellable, error);
 }
 
 /**
- * ostree_repo_pull_with_options:
- * @self: Repo
- * @remote_name: Name of remote
- * @options: A GVariant a{sv} with an extensible set of flags.
- * @progress: (allow-none): Progress
- * @cancellable: Cancellable
- * @error: Error
+ * _formatted_time_remaining_from_seconds
+ * @seconds_remaining: Estimated number of seconds remaining.
  *
- * Like ostree_repo_pull(), but supports an extensible set of flags.
- * The following are currently defined:
- *
- *   * refs (as): Array of string refs
- *   * flags (i): An instance of #OstreeRepoPullFlags
- *   * subdir (s): Pull just this subdirectory
- *   * override-remote-name (s): If local, add this remote to refspec
- *   * gpg-verify (b): GPG verify commits
- *   * gpg-verify-summary (b): GPG verify summary
- *   * depth (i): How far in the history to traverse; default is 0, -1 means infinite
- *   * disable-static-deltas (b): Do not use static deltas
- *   * require-static-deltas (b): Require static deltas
- *   * override-commit-ids (as): Array of specific commit IDs to fetch for refs
- *   * dry-run (b): Only print information on what will be downloaded (requires static deltas)
- */
-gboolean
-ostree_repo_pull_with_options (OstreeRepo             *self,
-                               const char             *remote_name,
-                               GVariant               *options,
-                               OstreeAsyncProgress    *progress,
-                               GCancellable           *cancellable,
-                               GError                **error)
+ * Returns a strings showing the number of days, hours, minutes
+ * and seconds remaining.
+ **/
+static char *
+_formatted_time_remaining_from_seconds (guint64 seconds_remaining)
 {
-  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                       "This version of ostree was built without libsoup, and cannot fetch over HTTP");
-  return FALSE;
-}
+  guint64 minutes_remaining = seconds_remaining / 60;
+  guint64 hours_remaining = minutes_remaining / 60;
+  guint64 days_remaining = hours_remaining / 24;
+
+  GString *description = g_string_new (NULL);
+
+  if (days_remaining)
+    g_string_append_printf (description, "%" G_GUINT64_FORMAT " days ", days_remaining);
+
+  if (hours_remaining)
+    g_string_append_printf (description, "%" G_GUINT64_FORMAT " hours ", hours_remaining % 24);
+
+  if (minutes_remaining)
+    g_string_append_printf (description, "%" G_GUINT64_FORMAT " minutes ", minutes_remaining % 60);
 
-#endif
+  if (seconds_remaining)
+    g_string_append_printf (description, "%" G_GUINT64_FORMAT " seconds ", seconds_remaining % 60);
+
+  return g_string_free (description, FALSE);
+}
 
 /**
  * ostree_repo_pull_default_console_progress_changed:
@@ -4126,12 +3820,15 @@ ostree_repo_pull_with_options (OstreeRepo             *self,
  * custom status message, or else outstanding fetch progress in bytes/sec,
  * or else outstanding content or metadata writes to the repository in
  * number of objects.
+ *
+ * Compatibility note: this function previously assumed that @user_data
+ * was a pointer to a #GSConsole instance.  This is no longer the case,
+ * and @user_data is ignored.
  **/
 void
 ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress,
                                                    gpointer             user_data)
 {
-  GSConsole *console = user_data;
   GString *buf;
   g_autofree char *status = NULL;
   guint outstanding_fetches;
@@ -4141,7 +3838,8 @@ ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress
   guint fetched_delta_parts;
   guint total_delta_parts;
 
-  if (!console)
+  /* Historical note; we used to treat this as a GSConsole instance */
+  if (user_data == NULL)
     return;
 
   buf = g_string_new ("");
@@ -4164,28 +3862,39 @@ ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress
       guint fetched = ostree_async_progress_get_uint (progress, "fetched");
       guint metadata_fetched = ostree_async_progress_get_uint (progress, "metadata-fetched");
       guint requested = ostree_async_progress_get_uint (progress, "requested");
-      guint64 bytes_sec = (g_get_monotonic_time () - ostree_async_progress_get_uint64 (progress, "start-time")) / G_USEC_PER_SEC;
+      guint64 start_time = ostree_async_progress_get_uint64 (progress, "start-time");
+      guint64 total_delta_part_size = ostree_async_progress_get_uint64 (progress, "total-delta-part-size");
+      guint64 current_time = g_get_monotonic_time ();
       g_autofree char *formatted_bytes_transferred =
         g_format_size_full (bytes_transferred, 0);
       g_autofree char *formatted_bytes_sec = NULL;
+      g_autofree char *formatted_est_time_remaining = NULL;
 
-      if (!bytes_sec) // Ignore first second
-        formatted_bytes_sec = g_strdup ("-");
+      /* Ignore the first second, or when we haven't transferred any
+       * data, since those could cause divide by zero below.
+       */
+      if ((current_time - start_time) < G_USEC_PER_SEC || bytes_transferred == 0)
+        {
+          formatted_bytes_sec = g_strdup ("-");
+          formatted_est_time_remaining = g_strdup ("- ");
+        }
       else
         {
-          bytes_sec = bytes_transferred / bytes_sec;
+          guint64 bytes_sec = bytes_transferred / ((current_time - start_time) / G_USEC_PER_SEC);
+          guint64 est_time_remaining =  (total_delta_part_size - bytes_transferred) / bytes_sec;
           formatted_bytes_sec = g_format_size (bytes_sec);
+          formatted_est_time_remaining = _formatted_time_remaining_from_seconds (est_time_remaining);
         }
 
       if (total_delta_parts > 0)
         {
-          guint64 total_delta_part_size = ostree_async_progress_get_uint64 (progress, "total-delta-part-size");
           g_autofree char *formatted_total =
             g_format_size (total_delta_part_size);
-          g_string_append_printf (buf, "Receiving delta parts: %u/%u %s/s %s/%s",
+          /* No space between %s and remaining, since formatted_est_time_remaining has a trailing space */
+          g_string_append_printf (buf, "Receiving delta parts: %u/%u %s/s %s/%s %sremaining",
                                   fetched_delta_parts, total_delta_parts,
                                   formatted_bytes_sec, formatted_bytes_transferred,
-                                  formatted_total);
+                                  formatted_total, formatted_est_time_remaining);
         }
       else if (outstanding_metadata_fetches)
         {
@@ -4208,7 +3917,7 @@ ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress
       g_string_append_printf (buf, "Scanning metadata: %u", n_scanned_metadata);
     }
 
-  gs_console_begin_status_line (console, buf->str, NULL, NULL);
+  glnx_console_text (buf->str);
 
   g_string_free (buf, TRUE);
 }
@@ -4233,7 +3942,6 @@ ostree_repo_append_gpg_signature (OstreeRepo     *self,
   gboolean ret = FALSE;
   g_autoptr(GVariant) metadata = NULL;
   g_autoptr(GVariant) new_metadata = NULL;
-  g_autoptr(GVariantBuilder) builder = NULL;
 
   if (!ostree_repo_read_commit_detached_metadata (self,
                                                   commit_checksum,
@@ -4359,7 +4067,7 @@ sign_data (OstreeRepo     *self,
   if (!g_output_stream_close (tmp_signature_output, cancellable, error))
     goto out;
   
-  signature_file = gs_file_map_noatime (tmp_signature_file, cancellable, error);
+  signature_file = g_mapped_file_new (gs_file_get_path_cached (tmp_signature_file), FALSE, error);
   if (!signature_file)
     goto out;
   ret_signature = g_mapped_file_get_bytes (signature_file);
@@ -4475,9 +4183,7 @@ out:
 
 /**
  * ostree_repo_sign_delta:
- * @self: Self
- * @from_commit: SHA256 of starting commit to sign, or %NULL
- * @to_commit: SHA256 of target commit to sign
+ *
  * This function is deprecated, sign the summary file instead.
  * Add a GPG signature to a static delta.
  */
@@ -4513,33 +4219,19 @@ ostree_repo_add_gpg_signature_summary (OstreeRepo     *self,
 {
   gboolean ret = FALSE;
   g_autoptr(GBytes) summary_data = NULL;
-  g_autoptr(GFile) summary_file = NULL;
-  g_autoptr(GFile) signature_path = NULL;
-  GError *temp_error = NULL;
   g_autoptr(GVariant) existing_signatures = NULL;
   g_autoptr(GVariant) new_metadata = NULL;
   g_autoptr(GVariant) normalized = NULL;
   guint i;
-  signature_path = g_file_resolve_relative_path (self->repodir, "summary.sig");
 
-  summary_file = g_file_resolve_relative_path (self->repodir, "summary");
-  summary_data = gs_file_map_readonly (summary_file, cancellable, error);
+  summary_data = ot_file_mapat_bytes (self->repo_dir_fd, "summary", error);
   if (!summary_data)
     goto out;
 
-  if (!ot_util_variant_map (signature_path, G_VARIANT_TYPE (OSTREE_SUMMARY_SIG_GVARIANT_STRING),
-                            TRUE, &existing_signatures, &temp_error))
-    {
-      if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
-        {
-          g_clear_error (&temp_error);
-        }
-      else
-        {
-          g_propagate_error (error, temp_error);
-          goto out;
-        }
-    }
+  if (!ot_util_variant_map_at (self->repo_dir_fd, "summary.sig",
+                               G_VARIANT_TYPE (OSTREE_SUMMARY_SIG_GVARIANT_STRING),
+                               OT_VARIANT_MAP_ALLOW_NOENT, &existing_signatures, error))
+    goto out;
 
   for (i = 0; key_id[i]; i++)
     {
@@ -4599,23 +4291,17 @@ find_keyring (OstreeRepo          *self,
   return NULL;
 }
 
-OstreeGpgVerifyResult *
-_ostree_repo_gpg_verify_with_metadata (OstreeRepo          *self,
-                                       GBytes              *signed_data,
-                                       GVariant            *metadata,
-                                       const char          *remote_name,
-                                       GFile               *keyringdir,
-                                       GFile               *extra_keyring,
-                                       GCancellable        *cancellable,
-                                       GError             **error)
+static OstreeGpgVerifyResult *
+_ostree_repo_gpg_verify_data_internal (OstreeRepo    *self,
+                                       const gchar   *remote_name,
+                                       GBytes        *data,
+                                       GBytes        *signatures,
+                                       GFile         *keyringdir,
+                                       GFile         *extra_keyring,
+                                       GCancellable  *cancellable,
+                                       GError       **error)
 {
-  OstreeGpgVerifyResult *result = NULL;
   glnx_unref_object OstreeGpgVerifier *verifier = NULL;
-  g_autoptr(GVariant) signaturedata = NULL;
-  GByteArray *buffer;
-  GVariantIter iter;
-  GVariant *child;
-  g_autoptr (GBytes) signatures = NULL;
   gboolean add_global_keyring_dir = TRUE;
 
   verifier = _ostree_gpg_verifier_new ();
@@ -4626,7 +4312,7 @@ _ostree_repo_gpg_verify_with_metadata (OstreeRepo          *self,
 
       if (!_ostree_gpg_verifier_add_keyring_dir (verifier, self->repodir,
                                                  cancellable, error))
-        goto out;
+        return NULL;
     }
   else if (remote_name != NULL)
     {
@@ -4637,7 +4323,7 @@ _ostree_repo_gpg_verify_with_metadata (OstreeRepo          *self,
 
       remote = ost_repo_get_remote_inherited (self, remote_name, error);
       if (remote == NULL)
-        goto out;
+        return NULL;
 
       file = find_keyring (self, remote, cancellable);
 
@@ -4654,20 +4340,43 @@ _ostree_repo_gpg_verify_with_metadata (OstreeRepo          *self,
     {
       /* Use the deprecated global keyring directory. */
       if (!_ostree_gpg_verifier_add_global_keyring_dir (verifier, cancellable, error))
-        goto out;
+        return NULL;
     }
 
   if (keyringdir)
     {
       if (!_ostree_gpg_verifier_add_keyring_dir (verifier, keyringdir,
                                                  cancellable, error))
-        goto out;
+        return NULL;
     }
   if (extra_keyring != NULL)
     {
       _ostree_gpg_verifier_add_keyring (verifier, extra_keyring);
     }
 
+  return _ostree_gpg_verifier_check_signature (verifier,
+                                               data,
+                                               signatures,
+                                               cancellable,
+                                               error);
+}
+
+OstreeGpgVerifyResult *
+_ostree_repo_gpg_verify_with_metadata (OstreeRepo          *self,
+                                       GBytes              *signed_data,
+                                       GVariant            *metadata,
+                                       const char          *remote_name,
+                                       GFile               *keyringdir,
+                                       GFile               *extra_keyring,
+                                       GCancellable        *cancellable,
+                                       GError             **error)
+{
+  g_autoptr(GVariant) signaturedata = NULL;
+  GByteArray *buffer;
+  GVariantIter iter;
+  GVariant *child;
+  g_autoptr (GBytes) signatures = NULL;
+
   if (metadata)
     signaturedata = g_variant_lookup_value (metadata,
                                             _OSTREE_METADATA_GPGSIGS_NAME,
@@ -4676,7 +4385,7 @@ _ostree_repo_gpg_verify_with_metadata (OstreeRepo          *self,
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
                    "GPG verification enabled, but no signatures found (use gpg-verify=false in remote config to disable)");
-      goto out;
+      return NULL;
     }
 
   /* OpenPGP data is organized into binary records called packets.  RFC 4880
@@ -4698,12 +4407,14 @@ _ostree_repo_gpg_verify_with_metadata (OstreeRepo          *self,
     }
   signatures = g_byte_array_free_to_bytes (buffer);
 
-  result = _ostree_gpg_verifier_check_signature (verifier,
-                                                 signed_data, signatures,
-                                                 cancellable, error);
-
- out:
-  return result;
+  return _ostree_repo_gpg_verify_data_internal (self,
+                                                remote_name,
+                                                signed_data,
+                                                signatures,
+                                                keyringdir,
+                                                extra_keyring,
+                                                cancellable,
+                                                error);
 }
 
 /* Needed an internal version for the remote_name parameter. */
@@ -4773,6 +4484,8 @@ out:
  *
  * Check for a valid GPG signature on commit named by the ASCII
  * checksum @commit_checksum.
+ *
+ * Returns: %TRUE if there was a GPG signature from a trusted keyring, otherwise %FALSE
  */
 gboolean
 ostree_repo_verify_commit (OstreeRepo   *self,
@@ -4783,25 +4496,12 @@ ostree_repo_verify_commit (OstreeRepo   *self,
                            GError      **error)
 {
   glnx_unref_object OstreeGpgVerifyResult *result = NULL;
-  gboolean ret = FALSE;
 
   result = ostree_repo_verify_commit_ext (self, commit_checksum,
                                           keyringdir, extra_keyring,
                                           cancellable, error);
-  if (result == NULL)
-    goto out;
-
-  if (ostree_gpg_verify_result_count_valid (result) == 0)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "GPG signatures found, but none are in trusted keyring");
-      goto out;
-    }
 
-  ret = TRUE;
-
- out:
-  return ret;
+  return ostree_gpg_verify_result_require_valid_signature (result, error);
 }
 
 /**
@@ -4835,6 +4535,49 @@ ostree_repo_verify_commit_ext (OstreeRepo    *self,
                                               error);
 }
 
+/**
+ * ostree_repo_gpg_verify_data:
+ * @self: Repository
+ * @remote_name: (nullable): Name of remote
+ * @data: Data as a #GBytes
+ * @signatures: Signatures as a #GBytes
+ * @keyringdir: (nullable): Path to directory GPG keyrings; overrides built-in default if given
+ * @extra_keyring: (nullable): Path to additional keyring file (not a directory)
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Verify @signatures for @data using GPG keys in the keyring for
+ * @remote_name, and return an #OstreeGpgVerifyResult.
+ *
+ * The @remote_name parameter can be %NULL. In that case it will do
+ * the verifications using GPG keys in the keyrings of all remotes.
+ *
+ * Returns: (transfer full): an #OstreeGpgVerifyResult, or %NULL on error
+ */
+OstreeGpgVerifyResult *
+ostree_repo_gpg_verify_data (OstreeRepo    *self,
+                             const gchar   *remote_name,
+                             GBytes        *data,
+                             GBytes        *signatures,
+                             GFile         *keyringdir,
+                             GFile         *extra_keyring,
+                             GCancellable  *cancellable,
+                             GError       **error)
+{
+  g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
+  g_return_val_if_fail (data != NULL, NULL);
+  g_return_val_if_fail (signatures != NULL, NULL);
+
+  return _ostree_repo_gpg_verify_data_internal (self,
+                                                (remote_name != NULL) ? remote_name : OSTREE_ALL_REMOTES,
+                                                data,
+                                                signatures,
+                                                keyringdir,
+                                                extra_keyring,
+                                                cancellable,
+                                                error);
+}
+
 /**
  * ostree_repo_verify_summary:
  * @self: Repo
@@ -4901,7 +4644,7 @@ ostree_repo_regenerate_summary (OstreeRepo     *self,
   g_autoptr(GVariant) summary = NULL;
   GList *ordered_keys = NULL;
   GList *iter = NULL;
-  GVariantDict additional_metadata_builder;
+  g_auto(GVariantDict) additional_metadata_builder = {{0,}};
 
   if (!ostree_repo_list_refs (self, NULL, &refs, cancellable, error))
     goto out;
@@ -4934,7 +4677,7 @@ ostree_repo_regenerate_summary (OstreeRepo     *self,
   {
     guint i;
     g_autoptr(GPtrArray) delta_names = NULL;
-    GVariantDict deltas_builder;
+    g_auto(GVariantDict) deltas_builder = {{0,}};
     g_autoptr(GVariant) deltas = NULL;
 
     if (!ostree_repo_list_static_delta_names (self, &delta_names, cancellable, error))
@@ -5010,6 +4753,50 @@ ostree_repo_regenerate_summary (OstreeRepo     *self,
   return ret;
 }
 
+gboolean
+_ostree_repo_is_locked_tmpdir (const char *filename)
+{
+  return g_str_has_prefix (filename, OSTREE_REPO_TMPDIR_STAGING) ||
+    g_str_has_prefix (filename, OSTREE_REPO_TMPDIR_FETCHER);
+}
+
+gboolean
+_ostree_repo_try_lock_tmpdir (int            tmpdir_dfd,
+                              const char    *tmpdir_name,
+                              GLnxLockFile  *file_lock_out,
+                              gboolean      *out_did_lock,
+                              GError       **error)
+{
+  gboolean ret = FALSE;
+  g_autofree char *lock_name = g_strconcat (tmpdir_name, "-lock", NULL);
+  gboolean did_lock = FALSE;
+  g_autoptr(GError) local_error = NULL;
+
+  /* We put the lock outside the dir, so we can hold the lock
+   * until the directory is fully removed */
+  if (!glnx_make_lock_file (tmpdir_dfd, lock_name, LOCK_EX | LOCK_NB,
+                            file_lock_out, &local_error))
+    {
+      if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+        {
+          did_lock = FALSE;
+        }
+      else
+        {
+          g_propagate_error (error, g_steal_pointer (&local_error));
+          goto out;
+        }
+    }
+  else
+    {
+      did_lock = TRUE;
+    }
+
+  ret = TRUE;
+  *out_did_lock = did_lock;
+ out:
+  return ret;
+}
 
 /* This allocates and locks a subdir of the repo tmp dir, using an existing
  * one with the same prefix if it is not in use already. */
@@ -5023,25 +4810,28 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
                               GCancellable *cancellable,
                               GError **error)
 {
+  gboolean ret = FALSE;
   gboolean reusing_dir = FALSE;
+  gboolean did_lock;
   g_autofree char *tmpdir_name = NULL;
   glnx_fd_close int tmpdir_fd = -1;
   g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
 
+  g_return_val_if_fail (_ostree_repo_is_locked_tmpdir (tmpdir_prefix), FALSE);
+
   /* Look for existing tmpdir (with same prefix) to reuse */
   if (!glnx_dirfd_iterator_init_at (tmpdir_dfd, ".", FALSE, &dfd_iter, error))
-    return FALSE;
+    goto out;
 
-  while (TRUE)
+  while (tmpdir_name == NULL)
     {
       gs_dirfd_iterator_cleanup GSDirFdIterator child_dfd_iter = { 0, };
       struct dirent *dent;
       glnx_fd_close int existing_tmpdir_fd = -1;
       g_autoptr(GError) local_error = NULL;
-      g_autofree char *lock_name = NULL;
 
       if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
-        return FALSE;
+        goto out;
 
       if (dent == NULL)
         break;
@@ -5062,25 +4852,18 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
           else
             {
               g_propagate_error (error, g_steal_pointer (&local_error));
-              return FALSE;
+              goto out;
             }
         }
 
-      lock_name = g_strconcat (dent->d_name, "-lock", NULL);
-
       /* We put the lock outside the dir, so we can hold the lock
        * until the directory is fully removed */
-      if (!glnx_make_lock_file (dfd_iter.fd, lock_name, LOCK_EX | LOCK_NB,
-                                file_lock_out, &local_error))
-        {
-          if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
-            continue;
-          else
-            {
-              g_propagate_error (error, g_steal_pointer (&local_error));
-              return FALSE;
-            }
-        }
+      if (!_ostree_repo_try_lock_tmpdir (dfd_iter.fd, dent->d_name,
+                                         file_lock_out, &did_lock,
+                                         error))
+        goto out;
+      if (!did_lock)
+        continue;
 
       /* Touch the reused directory so that we don't accidentally
        *   remove it due to being old when cleaning up the tmpdir
@@ -5098,32 +4881,24 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
       g_autofree char *tmpdir_name_template = g_strconcat (tmpdir_prefix, "XXXXXX", NULL);
       glnx_fd_close int new_tmpdir_fd = -1;
       g_autoptr(GError) local_error = NULL;
-      g_autofree char *lock_name = NULL;
 
       /* No existing tmpdir found, create a new */
 
       if (!glnx_mkdtempat (tmpdir_dfd, tmpdir_name_template, 0777, error))
-        return FALSE;
+        goto out;
 
       if (!glnx_opendirat (tmpdir_dfd, tmpdir_name_template, FALSE,
                            &new_tmpdir_fd, error))
-        return FALSE;
-
-      lock_name = g_strconcat (tmpdir_name_template, "-lock", NULL);
+        goto out;
 
       /* Note, at this point we can race with another process that picks up this
        * new directory. If that happens we need to retry, making a new directory. */
-      if (!glnx_make_lock_file (tmpdir_dfd, lock_name, LOCK_EX | LOCK_NB,
-                                file_lock_out, &local_error))
-        {
-          if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
-            continue;
-          else
-            {
-              g_propagate_error (error, g_steal_pointer (&local_error));
-              return FALSE;
-            }
-        }
+      if (!_ostree_repo_try_lock_tmpdir (tmpdir_dfd, tmpdir_name_template,
+                                         file_lock_out, &did_lock,
+                                         error))
+        goto out;
+      if (!did_lock)
+        continue;
 
       tmpdir_name = g_steal_pointer (&tmpdir_name_template);
       tmpdir_fd = glnx_steal_fd (&new_tmpdir_fd);
@@ -5138,5 +4913,7 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
   if (reusing_dir_out)
     *reusing_dir_out = reusing_dir;
 
-  return TRUE;
+  ret = TRUE;
+ out:
+  return ret;
 }
index 8a04e8e52f48ac3b18c3a79b43f2989fe1b9acdd..ce280ee66f6e775e356ef269d5da3ee3a8d19ef4 100644 (file)
@@ -115,6 +115,10 @@ gboolean      ostree_repo_remote_delete (OstreeRepo     *self,
                                          GCancellable   *cancellable,
                                          GError        **error);
 
+/**
+ * OstreeRepoRemoteChange:
+ * The remote change operation.
+ */
 typedef enum {
   OSTREE_REPO_REMOTE_CHANGE_ADD,
   OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS,
@@ -194,6 +198,15 @@ gboolean      ostree_repo_remote_fetch_summary (OstreeRepo    *self,
                                                 GCancellable  *cancellable,
                                                 GError       **error);
 
+_OSTREE_PUBLIC
+gboolean ostree_repo_remote_fetch_summary_with_options (OstreeRepo    *self,
+                                                        const char    *name,
+                                                        GVariant      *options,
+                                                        GBytes       **out_summary,
+                                                        GBytes       **out_signatures,
+                                                        GCancellable  *cancellable,
+                                                        GError       **error);
+
 _OSTREE_PUBLIC
 OstreeRepo * ostree_repo_get_parent (OstreeRepo  *self);
 
@@ -214,6 +227,10 @@ gboolean      ostree_repo_write_config (OstreeRepo *self,
  * were written to the repository in this transaction.
  * @content_bytes_written: The amount of data added to the repository,
  * in bytes, counting only content objects.
+ * @padding1: reserved
+ * @padding2: reserved
+ * @padding3: reserved
+ * @padding4: reserved
  *
  * A list of statistics for each transaction that may be
  * interesting for reporting purposes.
@@ -582,7 +599,9 @@ gboolean      ostree_repo_write_archive_to_mtree (OstreeRepo                   *
 typedef struct {
   guint ignore_unsupported_content : 1;
   guint autocreate_parents : 1;
-  guint reserved : 30;
+  guint use_ostree_convention : 1;
+  guint callback_with_entry_pathname : 1;
+  guint reserved : 28;
 
   guint unused_uint[8];
   gpointer unused_ptrs[8];
@@ -610,7 +629,10 @@ typedef struct {
   guint64 timestamp_secs;
 
   guint unused_uint[8];
-  gpointer unused_ptrs[8];
+
+  char *path_prefix;
+
+  gpointer unused_ptrs[7];
 } OstreeRepoExportArchiveOptions;
 
 _OSTREE_PUBLIC
@@ -685,7 +707,8 @@ typedef enum {
   OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES = 1
 } OstreeRepoCheckoutOverwriteMode;
 
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
 ostree_repo_checkout_tree (OstreeRepo               *self,
                            OstreeRepoCheckoutMode    mode,
                            OstreeRepoCheckoutOverwriteMode    overwrite_mode,
@@ -852,14 +875,16 @@ typedef enum {
   OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE = (1 << 0)
 } OstreeRepoCommitTraverseFlags;
 
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
 ostree_repo_commit_traverse_iter_init_commit (OstreeRepoCommitTraverseIter    *iter,
                                               OstreeRepo                      *repo,
                                               GVariant                        *commit,
                                               OstreeRepoCommitTraverseFlags    flags,
                                               GError                         **error);
 
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
 ostree_repo_commit_traverse_iter_init_dirtree (OstreeRepoCommitTraverseIter    *iter,
                                                OstreeRepo                      *repo,
                                                GVariant                        *dirtree,
@@ -889,8 +914,8 @@ void ostree_repo_commit_traverse_iter_get_dir (OstreeRepoCommitTraverseIter *ite
                                                char                        **out_content_checksum,
                                                char                        **out_meta_checksum);
 
-_OSTREE_PUBLIC void
-ostree_repo_commit_traverse_iter_clear (OstreeRepoCommitTraverseIter *iter);
+_OSTREE_PUBLIC
+void ostree_repo_commit_traverse_iter_clear (OstreeRepoCommitTraverseIter *iter);
 
 _OSTREE_PUBLIC
 void ostree_repo_commit_traverse_iter_cleanup (void *p);
@@ -909,7 +934,8 @@ typedef enum {
   OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY
 } OstreeRepoPruneFlags;
 
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
 ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit,
                                  GCancellable      *cancellable,
                                  GError           **error);
@@ -947,7 +973,8 @@ gboolean ostree_repo_pull (OstreeRepo             *self,
                            GCancellable           *cancellable,
                            GError                **error);
 
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
 ostree_repo_pull_one_dir (OstreeRepo               *self,
                           const char               *remote_name,
                           const char               *dir_to_pull,
@@ -986,7 +1013,8 @@ gboolean ostree_repo_sign_delta (OstreeRepo     *self,
                                  GCancellable   *cancellable,
                                  GError        **error);
 
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
 ostree_repo_add_gpg_signature_summary (OstreeRepo     *self,
                                        const gchar    **key_id,
                                        const gchar    *homedir,
@@ -1016,6 +1044,16 @@ OstreeGpgVerifyResult * ostree_repo_verify_commit_ext (OstreeRepo    *self,
                                                        GCancellable  *cancellable,
                                                        GError       **error);
 
+_OSTREE_PUBLIC
+OstreeGpgVerifyResult * ostree_repo_gpg_verify_data (OstreeRepo    *self,
+                                                     const gchar   *remote_name,
+                                                     GBytes        *data,
+                                                     GBytes        *signatures,
+                                                     GFile         *keyringdir,
+                                                     GFile         *extra_keyring,
+                                                     GCancellable  *cancellable,
+                                                     GError       **error);
+
 _OSTREE_PUBLIC
 OstreeGpgVerifyResult * ostree_repo_verify_summary (OstreeRepo    *self,
                                                     const char    *remote_name,
index 3b1a391bc3c8f90855bdd92d9e000ea796a2511d..8e49428f76384787a214c56a630dc2a123389a49 100644 (file)
@@ -32,7 +32,7 @@
 #include "ostree-bootloader-syslinux.h"
 
 /**
- * SECTION:libostree-sepolicy
+ * SECTION:ostree-sepolicy
  * @title: SELinux policy management
  * @short_description: Read SELinux policy and manage filesystem labels
  *
index 5c37050725ecdc7bd925848343d8a07f5c106aae..64c1389e16d14a67553e94bd356c6c6d686836ce 100644 (file)
@@ -32,7 +32,7 @@ _ostree_sysroot_list_deployment_dirs_for_os (GFile               *osdir,
                                              GError             **error)
 {
   gboolean ret = FALSE;
-  const char *osname = gs_file_get_basename_cached (osdir);
+  const char *osname = glnx_basename (gs_file_get_path_cached (osdir));
   g_autoptr(GFileEnumerator) dir_enum = NULL;
   g_autoptr(GFile) osdeploy_dir = NULL;
   GError *temp_error = NULL;
@@ -370,7 +370,7 @@ cleanup_old_deployments (OstreeSysroot       *self,
       g_autofree char *osname = NULL;
       g_autofree char *bootcsum = NULL;
 
-      if (!parse_bootdir_name (gs_file_get_basename_cached (bootdir),
+      if (!parse_bootdir_name (glnx_basename (gs_file_get_path_cached (bootdir)),
                                &osname, &bootcsum))
         g_assert_not_reached ();
 
index 77ecb9af125773eff9bdad6ade24aeabb82599aa..4616bab18f3629b6edcb002dd39ca0de815658ca 100644 (file)
@@ -87,6 +87,39 @@ symlink_at_replace (const char    *oldpath,
   return ret;
 }
 
+/* Try a hardlink if we can, otherwise fall back to copying.  Used
+ * right now for kernels/initramfs in /boot, where we can just
+ * hardlink if we're on the same partition.
+ */
+static gboolean
+hardlink_or_copy_at (int         src_dfd,
+                     const char *src_subpath,
+                     int         dest_dfd,
+                     const char *dest_subpath,
+                     GCancellable  *cancellable,
+                     GError       **error)
+{
+  gboolean ret = FALSE;
+
+  if (linkat (src_dfd, src_subpath, dest_dfd, dest_subpath, 0) != 0)
+    {
+      if (errno == EMLINK || errno == EXDEV)
+        {
+          return glnx_file_copy_at (src_dfd, src_subpath, NULL, dest_dfd, dest_subpath, 0,
+                                    cancellable, error);
+        }
+      else
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
 static gboolean
 dirfd_copy_attributes_and_xattrs (int            src_parent_dfd,
                                   const char    *src_name,
@@ -139,8 +172,8 @@ copy_dir_recurse (int              src_parent_dfd,
                   GError         **error)
 {
   gboolean ret = FALSE;
-  int src_dfd = -1;
-  int dest_dfd = -1;
+  glnx_fd_close int src_dfd = -1;
+  glnx_fd_close int dest_dfd = -1;
   DIR *srcd = NULL;
   struct dirent *dent;
 
@@ -207,10 +240,6 @@ copy_dir_recurse (int              src_parent_dfd,
       /* Note the srcd owns src_dfd */
       src_dfd = -1;
     }
-  if (src_dfd != -1)
-    (void) close (src_dfd);
-  if (dest_dfd != -1)
-    (void) close (dest_dfd);
   return ret;
 }
 
@@ -224,8 +253,8 @@ ensure_directory_from_template (int                 orig_etc_fd,
                                 GError            **error)
 {
   gboolean ret = FALSE;
-  int src_dfd = -1;
-  int target_dfd = -1;
+  glnx_fd_close int src_dfd = -1;
+  glnx_fd_close int target_dfd = -1;
 
   g_assert (path != NULL);
   g_assert (*path != '/' && *path != '\0');
@@ -283,10 +312,6 @@ ensure_directory_from_template (int                 orig_etc_fd,
       target_dfd = -1;
     }
  out:
-  if (src_dfd != -1)
-    (void) close (src_dfd);
-  if (target_dfd != -1)
-    (void) close (target_dfd);
   return ret;
 }
 
@@ -308,7 +333,7 @@ copy_modified_config_file (int                 orig_etc_fd,
   gboolean ret = FALSE;
   struct stat modified_stbuf;
   struct stat new_stbuf;
-  int dest_parent_dfd = -1;
+  glnx_fd_close int dest_parent_dfd = -1;
 
   if (fstatat (modified_etc_fd, path, &modified_stbuf, AT_SYMLINK_NOFOLLOW) < 0)
     {
@@ -398,8 +423,6 @@ copy_modified_config_file (int                 orig_etc_fd,
 
   ret = TRUE;
  out:
-  if (dest_parent_dfd != -1)
-    (void) close (dest_parent_dfd);
   return ret;
 }
 
@@ -426,9 +449,9 @@ merge_etc_changes (GFile          *orig_etc,
   g_autoptr(GPtrArray) removed = NULL;
   g_autoptr(GPtrArray) added = NULL;
   guint i;
-  int orig_etc_fd = -1;
-  int modified_etc_fd = -1;
-  int new_etc_fd = -1; 
+  glnx_fd_close int orig_etc_fd = -1;
+  glnx_fd_close int modified_etc_fd = -1;
+  glnx_fd_close int new_etc_fd = -1; 
 
   modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref);
   removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
@@ -505,12 +528,6 @@ merge_etc_changes (GFile          *orig_etc,
 
   ret = TRUE;
  out:
-  if (orig_etc_fd != -1)
-    (void) close (orig_etc_fd);
-  if (modified_etc_fd != -1)
-    (void) close (modified_etc_fd);
-  if (new_etc_fd != -1)
-    (void) close (new_etc_fd);
   return ret;
 }
 
@@ -651,7 +668,7 @@ relabel_recursively (OstreeSysroot  *sysroot,
       if (file_info == NULL)
         break;
 
-      g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (child));
+      g_ptr_array_add (path_parts, (char*)g_file_info_get_name (file_info));
 
       ftype = g_file_info_get_file_type (file_info);
       if (ftype == G_FILE_TYPE_DIRECTORY)
@@ -723,12 +740,12 @@ selinux_relabel_file (OstreeSysroot                 *sysroot,
     goto out;
 
   g_ptr_array_add (path_parts, (char*)prefix);
-  g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (path));
+  g_ptr_array_add (path_parts, (char*)g_file_info_get_name (file_info));
   if (!relabel_one_path (sysroot, sepolicy, path, file_info, path_parts,
                          cancellable, error))
     {
       g_prefix_error (error, "Relabeling /%s/%s: ", prefix,
-                      gs_file_get_basename_cached (path));
+                      g_file_info_get_name (file_info));
       goto out;
     }
 
@@ -1277,7 +1294,6 @@ install_deployment_kernel (OstreeSysroot   *sysroot,
   struct stat stbuf;
   const char *osname = ostree_deployment_get_osname (deployment);
   const char *bootcsum = ostree_deployment_get_bootcsum (deployment);
-  g_autoptr(GFile) bootdir = NULL;
   g_autofree char *bootcsumdir = NULL;
   g_autofree char *bootconfdir = NULL;
   g_autofree char *bootconf_name = NULL;
@@ -1293,10 +1309,6 @@ install_deployment_kernel (OstreeSysroot   *sysroot,
   g_autofree char *contents = NULL;
   g_autofree char *deployment_version = NULL;
   g_autoptr(GHashTable) osrelease_values = NULL;
-  g_autofree char *linux_relpath = NULL;
-  g_autofree char *linux_key = NULL;
-  g_autofree char *initramfs_relpath = NULL;
-  g_autofree char *initrd_key = NULL;
   g_autofree char *version_key = NULL;
   g_autofree char *ostree_kernel_arg = NULL;
   g_autofree char *options_key = NULL;
@@ -1342,9 +1354,9 @@ install_deployment_kernel (OstreeSysroot   *sysroot,
           glnx_set_prefix_error_from_errno (error, "fstat %s", dest_kernel_name);
           goto out;
         }
-      if (!glnx_file_copy_at (tree_boot_dfd, tree_kernel_name, NULL,
-                              bootcsum_dfd, dest_kernel_name, 0,
-                              cancellable, error))
+      if (!hardlink_or_copy_at (tree_boot_dfd, tree_kernel_name,
+                                bootcsum_dfd, dest_kernel_name,
+                                cancellable, error))
         goto out;
     }
 
@@ -1359,9 +1371,9 @@ install_deployment_kernel (OstreeSysroot   *sysroot,
               glnx_set_prefix_error_from_errno (error, "fstat %s", dest_initramfs_name);
               goto out;
             }
-          if (!glnx_file_copy_at (tree_boot_dfd, tree_initramfs_name, NULL,
-                                  bootcsum_dfd, dest_initramfs_name, 0,
-                                  cancellable, error))
+          if (!hardlink_or_copy_at (tree_boot_dfd, tree_initramfs_name,
+                                    bootcsum_dfd, dest_initramfs_name,
+                                    cancellable, error))
             goto out;
         }
     }
index d210a36f0d2d66ed3400ccd8489252cd1dbfc331..1fa8e83cff1bcfa7632f0b5d4be0029f76666ff2 100644 (file)
@@ -34,6 +34,10 @@ typedef enum {
 
 } OstreeSysrootDebugFlags;
 
+/**
+ * OstreeSysroot:
+ * Internal struct
+ */
 struct OstreeSysroot {
   GObject parent;
 
index 5c8f9d1d79b2cfc175b88518c154b8ecd081f96f..92b8dc8349f2d8c6ea41635d1ff002b0b9dfe5aa 100644 (file)
@@ -25,7 +25,7 @@
 #include "ostree-sysroot-upgrader.h"
 
 /**
- * SECTION:libostree-sysroot-upgrader
+ * SECTION:ostree-sysroot-upgrader
  * @title: Simple upgrade class
  * @short_description: Upgrade OSTree systems
  *
index b114a901b38fc078368f3321c6a487338fa57828..7cc4c9f509513f41953d7a41d246f3f82703ed6e 100644 (file)
@@ -37,7 +37,7 @@ find_booted_deployment (OstreeSysroot       *self,
                         GError             **error);
 
 /**
- * SECTION:libostree-sysroot
+ * SECTION:ostree-sysroot
  * @title: Root partition mount point
  * @short_description: Manage physical root filesystem
  *
@@ -70,10 +70,12 @@ ostree_sysroot_finalize (GObject *object)
   g_clear_object (&self->path);
   g_clear_object (&self->sepolicy);
   g_clear_object (&self->repo);
+  g_clear_pointer (&self->deployments, g_ptr_array_unref);
+  g_clear_object (&self->booted_deployment);
 
   glnx_release_lock_file (&self->lock);
 
-  (void) ostree_sysroot_unload (self);
+  ostree_sysroot_unload (self);
 
   G_OBJECT_CLASS (ostree_sysroot_parent_class)->finalize (object);
 }
@@ -276,28 +278,34 @@ ostree_sysroot_ensure_initialized (OstreeSysroot  *self,
                                    GError        **error)
 {
   gboolean ret = FALSE;
-  g_autoptr(GFile) dir = NULL;
-  g_autoptr(GFile) ostree_dir = NULL;
-  g_autoptr(GFile) repo_dir = NULL;
+  struct stat stbuf;
 
-  ostree_dir = g_file_get_child (self->path, "ostree");
-  repo_dir = g_file_get_child (ostree_dir, "repo");
-  if (!gs_file_ensure_directory (repo_dir, TRUE, cancellable, error))
+  if (!ensure_sysroot_fd (self, error))
     goto out;
 
-  g_clear_object (&dir);
-  dir = g_file_get_child (ostree_dir, "deploy");
-  if (!gs_file_ensure_directory (dir, TRUE, cancellable, error))
+  if (!glnx_shutil_mkdir_p_at (self->sysroot_fd, "ostree/repo", 0755,
+                               cancellable, error))
     goto out;
 
-  g_clear_object (&dir);
-  dir = ot_gfile_get_child_build_path (ostree_dir, "repo", "objects", NULL);
-  if (!g_file_query_exists (dir, NULL))
-    {
-      glnx_unref_object OstreeRepo *repo = ostree_repo_new (repo_dir);
-      if (!ostree_repo_create (repo, OSTREE_REPO_MODE_BARE,
+  if (!glnx_shutil_mkdir_p_at (self->sysroot_fd, "ostree/deploy", 0755,
                                cancellable, error))
-        goto out;
+    goto out;
+
+  if (fstatat (self->sysroot_fd, "ostree/repo/objects", &stbuf, 0) != 0)
+    {
+      if (errno != ENOENT)
+        {
+          glnx_set_prefix_error_from_errno (error, "stat %s", "ostree/repo/objects");
+          goto out;
+        }
+      else
+        {
+          g_autoptr(GFile) repo_dir = g_file_resolve_relative_path (self->path, "ostree/repo");
+          glnx_unref_object OstreeRepo *repo = ostree_repo_new (repo_dir);
+          if (!ostree_repo_create (repo, OSTREE_REPO_MODE_BARE,
+                                   cancellable, error))
+            goto out;
+        }
     }
 
   ret = TRUE;
@@ -873,7 +881,7 @@ ostree_sysroot_load_if_changed (OstreeSysroot  *self,
     }
 
   g_clear_pointer (&self->deployments, g_ptr_array_unref);
-  g_clear_pointer (&self->booted_deployment, g_object_unref);
+  g_clear_object (&self->booted_deployment);
   self->bootversion = -1;
   self->subbootversion = -1;
 
index f6bb6adbc4c1bba51d9a6e30f803f3133ef07022..9846a7e3a19af51b3c1a5dbde6628e776f825fb0 100644 (file)
@@ -31,3 +31,5 @@
 #include <ostree-bootconfig-parser.h>
 #include <ostree-diff.h>
 #include <ostree-gpg-verify-result.h>
+
+#include <ostree-autocleanups.h>
index 4d45cd060f27735bd6b11d316cc2d8187eed0a83..46a0405b8302de09a2b6aa4640f9efa5bb169568 100644 (file)
@@ -230,3 +230,24 @@ ot_openat_ignore_enoent (int dfd,
  out:
   return ret;
 }
+
+GBytes *
+ot_file_mapat_bytes (int dfd,
+                     const char *path,
+                     GError **error)
+{
+  glnx_fd_close int fd = openat (dfd, path, O_RDONLY | O_CLOEXEC);
+  g_autoptr(GMappedFile) mfile = NULL;
+
+  if (fd < 0)
+    {
+      glnx_set_error_from_errno (error);
+      return FALSE;
+    }
+
+  mfile = g_mapped_file_new_from_fd (fd, FALSE, error);
+  if (!mfile)
+    return FALSE;
+
+  return g_mapped_file_get_bytes (mfile);
+}
index cfeea74d42d830d85e861d7c0f78d1282cec8203..27f0f38ec60d4e181739236a1d05c8a1c8a92d9d 100644 (file)
@@ -66,4 +66,8 @@ gboolean ot_openat_ignore_enoent (int dfd,
                                   int *out_fd,
                                   GError **error);
 
+GBytes *ot_file_mapat_bytes (int dfd,
+                             const char *path,
+                             GError **error);
+
 G_END_DECLS
index 9b8b0dce10089cb4bb625f3ec34b34d19dab48f1..eb9b94f57f1424ff6798ceee1df232b74f2c3070 100644 (file)
@@ -254,7 +254,8 @@ ot_gfile_load_contents_utf8_allow_noent (GFile          *path,
   GError *temp_error = NULL;
   g_autofree char *ret_contents = NULL;
 
-  ret_contents = gs_file_load_contents_utf8 (path, cancellable, &temp_error);
+  ret_contents = glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (path), NULL,
+                                                 cancellable, &temp_error);
   if (!ret_contents)
     {
       if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
@@ -356,8 +357,8 @@ ot_gfile_replace_contents_fsync (GFile          *path,
                                  GError        **error)
 {
   gboolean ret = FALSE;
-  int parent_dfd;
-  const char *target_basename = gs_file_get_basename_cached (path);
+  glnx_fd_close int parent_dfd = -1;
+  const char *target_basename = glnx_basename (gs_file_get_path_cached (path));
   g_autoptr(GFile) parent = NULL;
 
   parent = g_file_get_parent (path);
@@ -373,8 +374,6 @@ ot_gfile_replace_contents_fsync (GFile          *path,
 
   ret = TRUE;
  out:
-  if (parent_dfd != -1)
-    (void) close (parent_dfd);
   return ret;
 }
 
@@ -389,25 +388,12 @@ ot_gfile_ensure_unlinked (GFile         *path,
                           GCancellable  *cancellable,
                           GError       **error)
 {
-  gboolean ret = FALSE;
-  GError *temp_error = NULL;
-
-  if (!gs_file_unlink (path, cancellable, &temp_error))
+  if (unlink (gs_file_get_path_cached (path)) != 0)
     {
-      if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
-        {
-          g_clear_error (&temp_error);
-        }
-      else
-        {
-          g_propagate_error (error, temp_error);
-          goto out;
-        }
+      if (errno != ENOENT)
+        return FALSE;
     }
-  
-  ret = TRUE;
- out:
-  return ret;
+  return TRUE;
 }
 
 /**
@@ -424,7 +410,7 @@ ot_util_fsync_directory (GFile         *dir,
                          GError       **error)
 {
   gboolean ret = FALSE;
-  int dfd = -1;
+  glnx_fd_close int dfd = -1;
 
   if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (dir), TRUE,
                        &dfd, error))
@@ -438,8 +424,6 @@ ot_util_fsync_directory (GFile         *dir,
 
   ret = TRUE;
  out:
-  if (dfd != -1)
-    (void) close (dfd);
   return ret;
 }
 
@@ -458,8 +442,8 @@ ot_util_ensure_directory_and_fsync (GFile         *dir,
                                     GError       **error)
 {
   gboolean ret = FALSE;
-  int parentfd = -1;
-  const char *basename = gs_file_get_basename_cached (dir);
+  glnx_fd_close int parentfd = -1;
+  const char *basename = glnx_basename (gs_file_get_path_cached (dir));
   g_autoptr(GFile) parent = g_file_get_parent (dir);
   
  again:
@@ -507,7 +491,5 @@ ot_util_ensure_directory_and_fsync (GFile         *dir,
 
   ret = TRUE;
  out:
-  if (parentfd != -1)
-    (void) close (parentfd);
   return ret;
 }
index 2dc07582c39065c4d29ab78058236199d592f55a..315bbeb22c2127c9a0f6dbc00f3ecaa3ad0bcfb2 100644 (file)
 GVariant *
 ot_gvariant_new_empty_string_dict (void)
 {
-  return g_variant_builder_end (g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")));
+  g_auto(GVariantBuilder) builder = {{0,}};
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+  return g_variant_builder_end (&builder);
 }
 
 GVariant *
@@ -114,63 +117,31 @@ ot_util_variant_take_ref (GVariant *variant)
   return g_variant_take_ref (variant);
 }
 
-/**
- * ot_util_variant_map:
- * @src: a #GFile
- * @type: Use this for variant
- * @trusted: See documentation of g_variant_new_from_data()
- * @out_variant: (out): Return location for new variant
- * @error:
- *
- * Memory-map @src, and store a new #GVariant referring to this memory
- * in @out_variant.  Note the returned @out_variant is not floating.
- */
-gboolean
-ot_util_variant_map (GFile              *src,
-                     const GVariantType *type,
-                     gboolean            trusted,
-                     GVariant          **out_variant,
-                     GError            **error)
-{
-  gboolean ret = FALSE;
-  g_autoptr(GVariant) ret_variant = NULL;
-  GMappedFile *mfile = NULL;
-
-  mfile = gs_file_map_noatime (src, NULL, error);
-  if (!mfile)
-    goto out;
-
-  ret_variant = g_variant_new_from_data (type,
-                                         g_mapped_file_get_contents (mfile),
-                                         g_mapped_file_get_length (mfile),
-                                         trusted,
-                                         (GDestroyNotify) g_mapped_file_unref,
-                                         mfile);
-  g_variant_ref_sink (ret_variant);
-  
-  ret = TRUE;
-  ot_transfer_out_value(out_variant, &ret_variant);
- out:
-  return ret;
-}
-
 gboolean
 ot_util_variant_map_at (int dfd,
                         const char *path,
                         const GVariantType *type,
-                        gboolean trusted,
+                        OtVariantMapFlags flags,
                         GVariant **out_variant,
                         GError  **error)
 {
   glnx_fd_close int fd = -1;
-  g_autoptr(GVariant) ret_variant = NULL;
+  const gboolean trusted = (flags & OT_VARIANT_MAP_TRUSTED) > 0;
 
   fd = openat (dfd, path, O_RDONLY | O_CLOEXEC);
   if (fd < 0)
     {
-      glnx_set_error_from_errno (error);
-      g_prefix_error (error, "Opening %s: ", path);
-      return FALSE;
+      if (errno == ENOENT && (flags & OT_VARIANT_MAP_ALLOW_NOENT) > 0)
+        {
+          *out_variant = NULL;
+          return TRUE;
+        }
+      else
+        {
+          glnx_set_error_from_errno (error);
+          g_prefix_error (error, "Opening %s: ", path);
+          return FALSE;
+        }
     }
 
   return ot_util_variant_map_fd (fd, 0, type, trusted, out_variant, error);
@@ -186,6 +157,7 @@ variant_map_data_destroy (gpointer data)
 {
   VariantMapData *mdata = data;
   (void) munmap (mdata->addr, mdata->len);
+  g_free (mdata);
 }
 
 gboolean
@@ -221,8 +193,8 @@ ot_util_variant_map_fd (int                    fd,
   mdata->len = len;
 
   ret = TRUE;
-  *out_variant = g_variant_new_from_data (type, map, len, trusted,
-                                          variant_map_data_destroy, mdata);
+  *out_variant = g_variant_ref_sink (g_variant_new_from_data (type, map, len, trusted,
+                                                              variant_map_data_destroy, mdata));
  out:
   return ret;
 }
index 1a7abe0e3a40c3b66b013a0d224fb15db60c039e..8a33cf604d46fb9e63731cb88328d04c50d44bc9 100644 (file)
@@ -42,16 +42,15 @@ gboolean ot_util_variant_save (GFile *dest,
                                GCancellable *cancellable,
                                GError  **error);
 
-gboolean ot_util_variant_map (GFile *src,
-                              const GVariantType *type,
-                              gboolean trusted,
-                              GVariant **out_variant,
-                              GError  **error);
+typedef enum {
+  OT_VARIANT_MAP_TRUSTED = (1 << 0),
+  OT_VARIANT_MAP_ALLOW_NOENT = (1 << 1)
+} OtVariantMapFlags;
 
 gboolean ot_util_variant_map_at (int dfd,
                                  const char *path,
                                  const GVariantType *type,
-                                 gboolean trusted,
+                                 OtVariantMapFlags flags,
                                  GVariant **out_variant,
                                  GError  **error);
 
index 0172f01c8e283119293ccdb016c24decd8917549..1f12255393b3d74846577bd7455b70b289ba612c 100644 (file)
@@ -39,8 +39,7 @@ ot_admin_builtin_init_fs (int argc, char **argv, GCancellable *cancellable, GErr
   GOptionContext *context;
   glnx_unref_object OstreeSysroot *sysroot = NULL;
   gboolean ret = FALSE;
-  g_autoptr(GFile) dir = NULL;
-  g_autoptr(GFile) child = NULL;
+  glnx_fd_close int root_dfd = -1;
   glnx_unref_object OstreeSysroot *target_sysroot = NULL;
   guint i;
   const char *normal_toplevels[] = {"boot", "dev", "home", "proc", "run", "sys"};
@@ -58,36 +57,31 @@ ot_admin_builtin_init_fs (int argc, char **argv, GCancellable *cancellable, GErr
       goto out;
     }
 
-  dir = g_file_new_for_path (argv[1]);
-  target_sysroot = ostree_sysroot_new (dir);
+  if (!glnx_opendirat (AT_FDCWD, argv[1], TRUE, &root_dfd, error))
+    goto out;
+  { g_autoptr(GFile) dir = g_file_new_for_path (argv[1]);
+    target_sysroot = ostree_sysroot_new (dir);
+  }
 
   for (i = 0; i < G_N_ELEMENTS(normal_toplevels); i++)
     {
-      child = g_file_get_child (dir, normal_toplevels[i]);
-      if (!gs_file_ensure_directory_mode (child, 0755, cancellable, error))
+      if (!glnx_shutil_mkdir_p_at (root_dfd, normal_toplevels[i], 0755,
+                                   cancellable, error))
         goto out;
-      g_clear_object (&child);
     }
-
-  child = g_file_get_child (dir, "root");
-  if (!gs_file_ensure_directory_mode (child, 0700, cancellable, error))
+  
+  if (!glnx_shutil_mkdir_p_at (root_dfd, "root", 0700,
+                               cancellable, error))
     goto out;
-  g_clear_object (&child);
 
-  child = g_file_get_child (dir, "tmp");
-  if (!gs_file_ensure_directory_mode (child, 01777, cancellable, error))
+  if (!glnx_shutil_mkdir_p_at (root_dfd, "tmp", 01777,
+                               cancellable, error))
     goto out;
-  /* FIXME - we should be using an API that explicitly ignores umask;
-   */
-  {
-    const char *path = gs_file_get_path_cached (child);
-    if (chmod (path, 01777) == -1)
-      {
-        gs_set_prefix_error_from_errno (error, errno, "chmod");
-        goto out;
-      }
-  }
-  g_clear_object (&child);
+  if (fchmodat (root_dfd, "tmp", 01777, 0) == -1)
+    {
+      glnx_set_prefix_error_from_errno (error, "chmod: %s", "tmp");
+      goto out;
+    }
 
   if (!ostree_sysroot_ensure_initialized (target_sysroot, cancellable, error))
     goto out;
index 1087b381021d9785dca2ca990fdbe558fe611512..88894a6af0be5225511a6d4911493c8e47a4accd 100644 (file)
 
 #include "config.h"
 
+#include "ot-main.h"
 #include "ot-builtins.h"
 #include "ot-admin-instutil-builtins.h"
 #include "ot-admin-builtins.h"
 #include "ot-admin-functions.h"
-#include "ot-main.h"
 #include "ostree.h"
 
 #include <glib/gi18n.h>
index 0e0101a9b8f38f8f46a30290fd9a461ec20c3d86..b313e830850f852dd65bb11b66d0d146ddb2105e 100644 (file)
@@ -61,8 +61,6 @@ ot_admin_builtin_switch (int argc, char **argv, GCancellable *cancellable, GErro
   glnx_unref_object OstreeSysrootUpgrader *upgrader = NULL;
   glnx_unref_object OstreeAsyncProgress *progress = NULL;
   gboolean changed;
-  GSConsole *console = NULL;
-  gboolean in_status_line = FALSE;
   GKeyFile *old_origin;
   GKeyFile *new_origin = NULL;
 
@@ -125,28 +123,24 @@ ot_admin_builtin_switch (int argc, char **argv, GCancellable *cancellable, GErro
   if (!ostree_sysroot_upgrader_set_origin (upgrader, new_origin, cancellable, error))
     goto out;
 
-  console = gs_console_get ();
-  if (console)
-    {
-      gs_console_begin_status_line (console, "", NULL, NULL);
-      in_status_line = TRUE;
-      progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
-    }
+  { g_auto(GLnxConsoleRef) console = { 0, };
+    glnx_console_lock (&console);
 
-  /* Always allow older...there's not going to be a chronological
-   * relationship necessarily.
-   */
-  if (!ostree_sysroot_upgrader_pull (upgrader, 0,
-                                     OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER,
-                                     progress, &changed,
-                                     cancellable, error))
-    goto out;
+    if (console.is_tty)
+      progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
 
-  if (in_status_line)
-    {
-      gs_console_end_status_line (console, NULL, NULL);
-      in_status_line = FALSE;
-    }
+    /* Always allow older...there's not going to be a chronological
+     * relationship necessarily.
+     */
+    if (!ostree_sysroot_upgrader_pull (upgrader, 0,
+                                       OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER,
+                                       progress, &changed,
+                                       cancellable, error))
+      goto out;
+
+    if (progress)
+      ostree_async_progress_finish (progress);
+  }
 
   if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error))
     goto out;
@@ -171,8 +165,6 @@ ot_admin_builtin_switch (int argc, char **argv, GCancellable *cancellable, GErro
 
   ret = TRUE;
  out:
-  if (in_status_line)
-    gs_console_end_status_line (console, NULL, NULL);
   if (new_origin)
     g_key_file_unref (new_origin);
   if (context)
index b3b531c0be47b23dc63035e8cf5fd578e1e06e1e..81f9bb6fb44b9e69903cbb50b035608319dc2696 100644 (file)
@@ -55,8 +55,6 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr
   g_autoptr(GFile) deployment_path = NULL;
   g_autoptr(GFile) deployment_origin_path = NULL;
   g_autoptr(GKeyFile) origin = NULL;
-  GSConsole *console = NULL;
-  gboolean in_status_line = FALSE;
   glnx_unref_object OstreeAsyncProgress *progress = NULL;
   gboolean changed;
   OstreeSysrootUpgraderPullFlags upgraderpullflags = 0;
@@ -108,27 +106,23 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr
         }
     }
 
-  console = gs_console_get ();
-  if (console)
-    {
-      gs_console_begin_status_line (console, "", NULL, NULL);
-      in_status_line = TRUE;
-      progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
-    }
+  { g_auto(GLnxConsoleRef) console = { 0, };
+    glnx_console_lock (&console);
 
-  if (opt_allow_downgrade)
-    upgraderpullflags |= OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER;
+    if (console.is_tty)
+      progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
 
-  if (!ostree_sysroot_upgrader_pull (upgrader, 0, upgraderpullflags,
-                                     progress, &changed,
-                                     cancellable, error))
-    goto out;
+    if (opt_allow_downgrade)
+      upgraderpullflags |= OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER;
+    
+    if (!ostree_sysroot_upgrader_pull (upgrader, 0, upgraderpullflags,
+                                       progress, &changed,
+                                       cancellable, error))
+      goto out;
 
-  if (in_status_line)
-    {
-      gs_console_end_status_line (console, NULL, NULL);
-      in_status_line = FALSE;
-    }
+    if (progress)
+      ostree_async_progress_finish (progress);
+  }
 
   if (!changed)
     {
@@ -148,8 +142,6 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr
 
   ret = TRUE;
  out:
-  if (in_status_line)
-    gs_console_end_status_line (console, NULL, NULL);
   if (context)
     g_option_context_free (context);
   return ret;
index bc9034e136f7195e6af0042cd4dfb279272aa4ce..ed4dfdfbca7486f0ee8dcf522b11871e719815b9 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "config.h"
 
+#include "libglnx.h"
 #include "ot-admin-functions.h"
 #include "otutil.h"
 #include "ostree.h"
index 1f0e91e34af8c37277352b3a97ad1f64c0cbe44a..776ab54f07041aaaa6ac2dc864783b6e2352b53e 100644 (file)
@@ -120,7 +120,7 @@ relabel_recursively (OstreeSePolicy *sepolicy,
       if (file_info == NULL)
         break;
 
-      g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (child));
+      g_ptr_array_add (path_parts, (char*)g_file_info_get_name (file_info));
 
       ftype = g_file_info_get_file_type (file_info);
       if (ftype == G_FILE_TYPE_DIRECTORY)
index 8b866170a344ccca49105a09f7d987194d02371a..7e7b04b577be73b720b5434f2be641e00757c6f9 100644 (file)
 
 #include "config.h"
 
+#include "ot-main.h"
 #include "ot-builtins.h"
 #include "ot-admin-builtins.h"
 #include "ot-admin-functions.h"
-#include "ot-main.h"
 #include "ostree.h"
 #include "ostree-repo-file.h"
 
index 1d96855de37141c77464ea4970fdc85ce14bc6a2..85c4c65d0a51e6035f01414f1b4e3c557a92db2b 100644 (file)
 
 static char *opt_subject;
 static char *opt_body;
+static gboolean opt_editor;
 static char *opt_parent;
 static gboolean opt_orphan;
 static char *opt_branch;
 static char *opt_statoverride_file;
+static char *opt_skiplist_file;
 static char **opt_metadata_strings;
 static char **opt_detached_metadata_strings;
 static gboolean opt_link_checkout_speedup;
@@ -72,6 +74,7 @@ static GOptionEntry options[] = {
   { "parent", 0, 0, G_OPTION_ARG_STRING, &opt_parent, "Parent ref, or \"none\"", "REF" },
   { "subject", 's', 0, G_OPTION_ARG_STRING, &opt_subject, "One line subject", "SUBJECT" },
   { "body", 'm', 0, G_OPTION_ARG_STRING, &opt_body, "Full description", "BODY" },
+  { "editor", 'e', 0, G_OPTION_ARG_NONE, &opt_editor, "Use an editor to write the commit message", NULL },
   { "branch", 'b', 0, G_OPTION_ARG_STRING, &opt_branch, "Branch", "BRANCH" },
   { "orphan", 0, 0, G_OPTION_ARG_NONE, &opt_orphan, "Create a commit without writing a ref", NULL },
   { "tree", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_trees, "Overlay the given argument as a tree", "dir=PATH or tar=TARFILE or ref=COMMIT" },
@@ -84,6 +87,7 @@ static GOptionEntry options[] = {
   { "tar-autocreate-parents", 0, 0, G_OPTION_ARG_NONE, &opt_tar_autocreate_parents, "When loading tar archives, automatically create parent directories as needed", NULL },
   { "skip-if-unchanged", 0, 0, G_OPTION_ARG_NONE, &opt_skip_if_unchanged, "If the contents are unchanged from previous commit, do nothing", NULL },
   { "statoverride", 0, 0, G_OPTION_ARG_FILENAME, &opt_statoverride_file, "File containing list of modifications to make to permissions", "PATH" },
+  { "skip-list", 0, 0, G_OPTION_ARG_FILENAME, &opt_skiplist_file, "File containing list of files to skip", "PATH" },
   { "table-output", 0, 0, G_OPTION_ARG_NONE, &opt_table_output, "Output more information in a KEY: VALUE format", NULL },
   { "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "GPG Key ID to sign the commit with", "KEY-ID"},
   { "gpg-homedir", 0, 0, G_OPTION_ARG_STRING, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR"},
@@ -95,65 +99,85 @@ static GOptionEntry options[] = {
 };
 
 static gboolean
-parse_statoverride_file (GHashTable   **out_mode_add,
-                         GCancellable  *cancellable,
-                         GError        **error)
+parse_file_by_line (const char    *path,
+                    gboolean     (*cb)(const char*, void*, GError**),
+                    void          *cbdata,
+                    GCancellable  *cancellable,
+                    GError       **error)
 {
   gboolean ret = FALSE;
-  gsize len;
-  char **iter = NULL; /* nofree */
-  g_autoptr(GHashTable) ret_hash = NULL;
-  g_autoptr(GFile) path = NULL;
   g_autofree char *contents = NULL;
+  g_autoptr(GFile) file = NULL;
   char **lines = NULL;
 
-  path = g_file_new_for_path (opt_statoverride_file);
-
-  if (!g_file_load_contents (path, cancellable, &contents, &len, NULL,
-                             error))
+  file = g_file_new_for_path (path);
+  if (!g_file_load_contents (file, cancellable, &contents, NULL, NULL, error))
     goto out;
-  
-  ret_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-  lines = g_strsplit (contents, "\n", -1);
 
-  for (iter = lines; iter && *iter; iter++)
+  lines = g_strsplit (contents, "\n", -1);
+  for (char **iter = lines; iter && *iter; iter++)
     {
-      const char *line = *iter;
-
-      if (*line == '+')
-        {
-          const char *spc;
-          guint mode_add;
+      /* skip empty lines at least */
+      if (**iter == '\0')
+        continue;
 
-          spc = strchr (line + 1, ' ');
-          if (!spc)
-            {
-              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "Malformed statoverride file");
-              goto out;
-            }
-          
-          mode_add = (guint32)(gint32)g_ascii_strtod (line + 1, NULL);
-          g_hash_table_insert (ret_hash,
-                               g_strdup (spc + 1),
-                               GUINT_TO_POINTER((gint32)mode_add));
-        }
+      if (!cb (*iter, cbdata, error))
+        goto out;
     }
 
   ret = TRUE;
-  ot_transfer_out_value (out_mode_add, &ret_hash);
- out:
+out:
   g_strfreev (lines);
   return ret;
 }
 
+static gboolean
+handle_statoverride_line (const char  *line,
+                          void        *data,
+                          GError     **error)
+{
+  GHashTable *files = data;
+  const char *spc;
+  guint mode_add;
+
+  spc = strchr (line, ' ');
+  if (spc == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Malformed statoverride file (no space found)");
+      return FALSE;
+    }
+
+  mode_add = (guint32)(gint32)g_ascii_strtod (line, NULL);
+  g_hash_table_insert (files, g_strdup (spc + 1),
+                       GUINT_TO_POINTER((gint32)mode_add));
+  return TRUE;
+}
+
+static gboolean
+handle_skiplist_line (const char  *line,
+                      void        *data,
+                      GError     **error)
+{
+  GHashTable *files = data;
+  g_hash_table_add (files, g_strdup (line));
+  return TRUE;
+}
+
+struct CommitFilterData {
+  GHashTable *mode_adds;
+  GHashTable *skip_list;
+};
+
 static OstreeRepoCommitFilterResult
 commit_filter (OstreeRepo         *self,
                const char         *path,
                GFileInfo          *file_info,
                gpointer            user_data)
 {
-  GHashTable *mode_adds = user_data;
+  struct CommitFilterData *data = user_data;
+  GHashTable *mode_adds = data->mode_adds;
+  GHashTable *skip_list = data->skip_list;
   gpointer value;
 
   if (opt_owner_uid >= 0)
@@ -169,7 +193,13 @@ commit_filter (OstreeRepo         *self,
                                         current_mode | mode_add);
       g_hash_table_remove (mode_adds, path);
     }
-  
+
+  if (skip_list && g_hash_table_contains (skip_list, path))
+    {
+      g_hash_table_remove (skip_list, path);
+      return OSTREE_REPO_COMMIT_FILTER_SKIP;
+    }
+
   return OSTREE_REPO_COMMIT_FILTER_ALLOW;
 }
 
@@ -188,14 +218,18 @@ commit_editor (OstreeRepo     *repo,
   char **lines = NULL;
   int i;
 
-  *subject = NULL;
-  *body = NULL;
-
   input = g_strdup_printf ("\n"
       "# Please enter the commit message for your changes. The first line will\n"
       "# become the subject, and the remainder the body. Lines starting\n"
       "# with '#' will be ignored, and an empty message aborts the commit."
-      "%s%s\n", branch ? "\n#\n# Branch: " : "", branch ?: "");
+      "%s%s%s%s%s%s\n"
+              , branch ? "\n#\n# Branch: " : "", branch ? branch : ""
+              , *subject ? "\n" : "", *subject ? *subject : ""
+              , *body ? "\n" : "", *body ? *body : ""
+              );
+
+  *subject = NULL;
+  *body = NULL;
 
   output = ot_editor_prompt (repo, input, cancellable, error);
   if (output == NULL)
@@ -310,9 +344,11 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
   glnx_unref_object OstreeMutableTree *mtree = NULL;
   g_autofree char *tree_type = NULL;
   g_autoptr(GHashTable) mode_adds = NULL;
+  g_autoptr(GHashTable) skip_list = NULL;
   OstreeRepoCommitModifierFlags flags = 0;
   OstreeRepoCommitModifier *modifier = NULL;
   OstreeRepoTransactionStats stats;
+  struct CommitFilterData filter_data = { 0, };
 
   context = g_option_context_new ("[PATH] - Commit a new revision");
 
@@ -324,7 +360,17 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
 
   if (opt_statoverride_file)
     {
-      if (!parse_statoverride_file (&mode_adds, cancellable, error))
+      mode_adds = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+      if (!parse_file_by_line (opt_statoverride_file, handle_statoverride_line,
+                               mode_adds, cancellable, error))
+        goto out;
+    }
+
+  if (opt_skiplist_file)
+    {
+      skip_list = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+      if (!parse_file_by_line (opt_skiplist_file, handle_skiplist_line,
+                               skip_list, cancellable, error))
         goto out;
     }
 
@@ -359,9 +405,13 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
       || opt_owner_uid >= 0
       || opt_owner_gid >= 0
       || opt_statoverride_file != NULL
+      || opt_skiplist_file != NULL
       || opt_no_xattrs)
     {
-      modifier = ostree_repo_commit_modifier_new (flags, commit_filter, mode_adds, NULL);
+      filter_data.mode_adds = mode_adds;
+      filter_data.skip_list = skip_list;
+      modifier = ostree_repo_commit_modifier_new (flags, commit_filter,
+                                                  &filter_data, NULL);
     }
 
   if (opt_parent)
@@ -381,19 +431,12 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
         goto out;
     }
 
-  if (!opt_subject && !opt_body)
+  if (opt_editor)
     {
       if (!commit_editor (repo, opt_branch, &opt_subject, &opt_body, cancellable, error))
         goto out;
     }
 
-  if (!opt_subject)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "A subject must be specified with --subject");
-      goto out;
-    }
-
   if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
     goto out;
 
@@ -491,6 +534,22 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
       goto out;
     }
 
+  if (skip_list && g_hash_table_size (skip_list) > 0)
+    {
+      GHashTableIter hash_iter;
+      gpointer key;
+
+      g_hash_table_iter_init (&hash_iter, skip_list);
+
+      while (g_hash_table_iter_next (&hash_iter, &key, NULL))
+        {
+          g_printerr ("Unmatched skip-list path: %s\n", (char*)key);
+        }
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Unmatched skip-list paths");
+      goto out;
+    }
+
   if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error))
     goto out;
 
index cccb50e727dd80ef58bf32fa846e0f6b6b463df4..5b84d1ab51a235c5735214e1879c1c5ee20f6dc9 100644 (file)
 #endif
 
 static char *opt_output_path;
+static char *opt_subpath;
+static char *opt_prefix;
 static gboolean opt_no_xattrs;
 
 static GOptionEntry options[] = {
   { "no-xattrs", 0, 0, G_OPTION_ARG_NONE, &opt_no_xattrs, "Skip output of extended attributes", NULL },
+  { "subpath", 0, 0, G_OPTION_ARG_STRING, &opt_subpath, "Checkout sub-directory PATH", "PATH" },
+  { "prefix", 0, 0, G_OPTION_ARG_STRING, &opt_prefix, "Add PATH as prefix to archive pathnames", "PATH" },
   { "output", 'o', 0, G_OPTION_ARG_STRING, &opt_output_path, "Output to PATH ", "PATH" },
   { NULL }
 };
@@ -60,6 +64,7 @@ ostree_builtin_export (int argc, char **argv, GCancellable *cancellable, GError
   gboolean ret = FALSE;
   const char *rev;
   g_autoptr(GFile) root = NULL;
+  g_autoptr(GFile) subtree = NULL;
   g_autofree char *commit = NULL;
   g_autoptr(GVariant) commit_data = NULL;
   struct archive *a;
@@ -124,7 +129,14 @@ ostree_builtin_export (int argc, char **argv, GCancellable *cancellable, GError
 
   opts.timestamp_secs = ostree_commit_get_timestamp (commit_data);
 
-  if (!ostree_repo_export_tree_to_archive (repo, &opts, (OstreeRepoFile*)root, a,
+  if (opt_subpath)
+    subtree = g_file_resolve_relative_path (root, opt_subpath);
+  else
+    subtree = g_object_ref (root);
+
+  opts.path_prefix = opt_prefix;
+
+  if (!ostree_repo_export_tree_to_archive (repo, &opts, (OstreeRepoFile*)subtree, a,
                                            cancellable, error))
     goto out;
 
index 9e7c8a2bfedfe8a1e612eda87ca23ca252cfb878..a250b793488b6b1f0e86d412e0974bb5629b8a3f 100644 (file)
@@ -37,7 +37,7 @@ gboolean
 ostree_builtin_init (int argc, char **argv, GCancellable *cancellable, GError **error)
 {
   GOptionContext *context = NULL;
-  glnx_unref_object OstreeRepo *repo = NULL;
+  g_autoptr(OstreeRepo) repo = NULL;
   gboolean ret = FALSE;
   OstreeRepoMode mode;
 
index 36057ec65bbe0462c3ff4b27ff40344cde4d1f1d..7fb6f03ab1cfbb13cccda3bc9a47c9b241c68aac 100644 (file)
@@ -55,7 +55,6 @@ ostree_builtin_pull_local (int argc, char **argv, GCancellable *cancellable, GEr
   glnx_unref_object OstreeRepo *repo = NULL;
   int i;
   const char *src_repo_arg;
-  GSConsole *console = NULL;
   g_autofree char *src_repo_uri = NULL;
   glnx_unref_object OstreeAsyncProgress *progress = NULL;
   g_autoptr(GPtrArray) refs_to_fetch = NULL;
@@ -132,14 +131,11 @@ ostree_builtin_pull_local (int argc, char **argv, GCancellable *cancellable, GEr
       g_ptr_array_add (refs_to_fetch, NULL);
     }
 
-  console = gs_console_get ();
-  if (console)
-    {
-      gs_console_begin_status_line (console, "", NULL, NULL);
-      progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
-    }
-
   { GVariantBuilder builder;
+    g_auto(GLnxConsoleRef) console = { 0, };
+
+    glnx_console_lock (&console);
+
     g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
 
     g_variant_builder_add (&builder, "{s@v}", "flags",
@@ -158,17 +154,21 @@ ostree_builtin_pull_local (int argc, char **argv, GCancellable *cancellable, GEr
     g_variant_builder_add (&builder, "{s@v}", "depth",
                            g_variant_new_variant (g_variant_new_int32 (opt_depth)));
 
+    if (console.is_tty)
+      progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
+
     if (!ostree_repo_pull_with_options (repo, src_repo_uri, 
                                         g_variant_builder_end (&builder),
                                         progress,
                                         cancellable, error))
       goto out;
+
+    if (progress)
+      ostree_async_progress_finish (progress);
   }
 
   ret = TRUE;
  out:
-  if (progress)
-    ostree_async_progress_finish (progress);
   if (context)
     g_option_context_free (context);
   if (repo)
index 734f744035a4fd19e8c3739541e6ce7a5b6497c9..99b25937a2f950446ac9f1d6511ef951b7190de0 100644 (file)
@@ -37,7 +37,8 @@ static gboolean opt_untrusted;
 static char* opt_subpath;
 static char* opt_cache_dir;
 static int opt_depth = 0;
+static char* opt_url;
+
 static GOptionEntry options[] = {
    { "commit-metadata-only", 0, 0, G_OPTION_ARG_NONE, &opt_commit_only, "Fetch only the commit metadata", NULL },
    { "cache-dir", 0, 0, G_OPTION_ARG_STRING, &opt_cache_dir, "Use custom cache dir", NULL },
@@ -49,6 +50,7 @@ static GOptionEntry options[] = {
    { "untrusted", 0, 0, G_OPTION_ARG_NONE, &opt_untrusted, "Do not trust (local) sources", NULL },
    { "dry-run", 0, 0, G_OPTION_ARG_NONE, &opt_dry_run, "Only print information on what will be downloaded (requires static deltas)", NULL },
    { "depth", 0, 0, G_OPTION_ARG_INT, &opt_depth, "Traverse DEPTH parents (-1=infinite) (default: 0)", "DEPTH" },
+   { "url", 0, 0, G_OPTION_ARG_STRING, &opt_url, "Pull objects from this URL instead of the one from the remote config", NULL },
    { NULL }
  };
 
@@ -56,16 +58,17 @@ static void
 gpg_verify_result_cb (OstreeRepo *repo,
                       const char *checksum,
                       OstreeGpgVerifyResult *result,
-                      GSConsole *console)
+                      GLnxConsoleRef *console)
 {
-  /* Temporarily place the GSConsole stream (which is just stdout)
-   * back in normal mode before printing GPG verification results. */
-  gs_console_end_status_line (console, NULL, NULL);
+  /* Temporarily place the tty back in normal mode before printing GPG
+   * verification results.
+   */
+  glnx_console_unlock (console);
 
   g_print ("\n");
   ostree_print_gpg_verify_result (result);
 
-  gs_console_begin_status_line (console, "", NULL, NULL);
+  glnx_console_lock (console);
 }
 
 static gboolean printed_console_progress;
@@ -109,7 +112,6 @@ ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **
   gboolean ret = FALSE;
   g_autofree char *remote = NULL;
   OstreeRepoPullFlags pullflags = 0;
-  GSConsole *console = NULL;
   g_autoptr(GPtrArray) refs_to_fetch = NULL;
   g_autoptr(GPtrArray) override_commit_ids = NULL;
   glnx_unref_object OstreeAsyncProgress *progress = NULL;
@@ -204,30 +206,16 @@ ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **
       g_ptr_array_add (refs_to_fetch, NULL);
     }
 
-  if (!opt_dry_run)
-    {
-      console = gs_console_get ();
-      if (console)
-        {
-          gs_console_begin_status_line (console, "", NULL, NULL);
-          progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
-          signal_handler_id = g_signal_connect (repo, "gpg-verify-result",
-                                                G_CALLBACK (gpg_verify_result_cb),
-                                                console);
-        }
-    }
-  else
-    {
-      progress = ostree_async_progress_new_and_connect (dry_run_console_progress_changed, console);
-      signal_handler_id = g_signal_connect (repo, "gpg-verify-result",
-                                            G_CALLBACK (gpg_verify_result_cb),
-                                            console);
-    }
-
   {
     GVariantBuilder builder;
+    g_auto(GLnxConsoleRef) console = { 0, };
     g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
 
+    glnx_console_lock (&console);
+
+    if (opt_url)
+      g_variant_builder_add (&builder, "{s@v}", "override-url",
+                             g_variant_new_variant (g_variant_new_string (opt_url)));
     if (opt_subpath)
       g_variant_builder_add (&builder, "{s@v}", "subdir",
                              g_variant_new_variant (g_variant_new_string (opt_subpath)));
@@ -252,25 +240,38 @@ ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **
       g_variant_builder_add (&builder, "{s@v}", "override-commit-ids",
                              g_variant_new_variant (g_variant_new_strv ((const char*const*)override_commit_ids->pdata, override_commit_ids->len)));
 
+    if (!opt_dry_run)
+      {
+        if (console.is_tty)
+          progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
+      }
+    else
+      {
+        progress = ostree_async_progress_new_and_connect (dry_run_console_progress_changed, NULL);
+      }
+
+    if (console.is_tty)
+      {
+        signal_handler_id = g_signal_connect (repo, "gpg-verify-result",
+                                              G_CALLBACK (gpg_verify_result_cb),
+                                              &console);
+      }
+
     if (!ostree_repo_pull_with_options (repo, remote, g_variant_builder_end (&builder),
                                         progress, cancellable, error))
       goto out;
-  }
 
-  if (progress)
-    ostree_async_progress_finish (progress);
+    if (progress)
+      ostree_async_progress_finish (progress);
 
-  if (opt_dry_run)
-    g_assert (printed_console_progress);
+    if (opt_dry_run)
+      g_assert (printed_console_progress);
+  }
 
   ret = TRUE;
  out:
   if (signal_handler_id > 0)
     g_signal_handler_disconnect (repo, signal_handler_id);
-
-  if (console)
-    gs_console_end_status_line (console, NULL, NULL);
   if (context)
     g_option_context_free (context);
   return ret;
index af90c841e9767ae79654465b716c4720f0cb5df5..10647ec639e038dbea208e856c26e32e65dfb7c7 100644 (file)
 
 static gboolean opt_delete;
 static gboolean opt_list;
+static char *opt_create;
 
 static GOptionEntry options[] = {
   { "delete", 0, 0, G_OPTION_ARG_NONE, &opt_delete, "Delete refs which match PREFIX, rather than listing them", NULL },
   { "list", 0, 0, G_OPTION_ARG_NONE, &opt_list, "Do not remove the prefix from the refs", NULL },
+  { "create", 0, 0, G_OPTION_ARG_STRING, &opt_create, "Create a new ref for an existing commit", "NEWREF" },
   { NULL }
 };
 
@@ -48,10 +50,16 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab
                                       cancellable, error))
         goto out;
     }
+  else if (opt_create)
+    {
+      if (!ostree_repo_list_refs_ext (repo, NULL, &refs, OSTREE_REPO_LIST_REFS_EXT_NONE,
+                                      cancellable, error))
+        goto out;
+    }
   else if (!ostree_repo_list_refs (repo, refspec_prefix, &refs, cancellable, error))
     goto out;
 
-  if (!opt_delete)
+  if (!opt_delete && !opt_create)
     {
       g_hash_table_iter_init (&hashiter, refs);
       while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
@@ -60,7 +68,30 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab
           g_print ("%s\n", ref);
         }
     }
+  else if (opt_create)
+    {
+      g_autofree char *checksum = NULL;
+      g_autofree char *checksum_existing = NULL;
+
+      if (!ostree_repo_resolve_rev (repo, opt_create, TRUE, &checksum_existing, error))
+        goto out;
+
+      if (checksum_existing != NULL)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "--create specified but ref %s already exists", opt_create);
+          goto out;
+        }
+
+      if (!ostree_repo_resolve_rev (repo, refspec_prefix, FALSE, &checksum, error))
+        goto out;
+
+      if (!ostree_repo_set_ref_immediate (repo, NULL, opt_create, checksum,
+                                          cancellable, error))
+        goto out;
+    }
   else
+    /* delete */
     {
       g_hash_table_iter_init (&hashiter, refs);
       while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
@@ -97,6 +128,12 @@ ostree_builtin_refs (int argc, char **argv, GCancellable *cancellable, GError **
 
   if (argc >= 2)
     {
+      if (opt_create && argc > 2)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "You must specify only 1 existing ref when creating a new ref");
+          goto out;
+        }
       for (i = 1; i < argc; i++)
         if (!do_ref (repo, argv[i], cancellable, error))
           goto out;
@@ -110,6 +147,13 @@ ostree_builtin_refs (int argc, char **argv, GCancellable *cancellable, GError **
                        "At least one PREFIX is required when deleting refs");
           goto out;
         }
+      else if (opt_create)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "You must specify an existing ref when creating a new ref");
+          goto out;
+        }
+
       ret = do_ref (repo, NULL, cancellable, error);
     }
 
index a1b4db5ffa583b4b8b5b4c6ee8cef02b9d64e0dd..ef541c2a7ac957d4da92e5f58c0aa723c55b86e7 100644 (file)
@@ -51,12 +51,9 @@ do_print_variant_generic (const GVariantType *type,
                           GError **error)
 {
   gboolean ret = FALSE;
-  g_autoptr(GFile) f = NULL;
   g_autoptr(GVariant) variant = NULL;
 
-  f = g_file_new_for_path (filename);
-
-  if (!ot_util_variant_map (f, type, TRUE, &variant, error))
+  if (!ot_util_variant_map_at (AT_FDCWD, filename, type, TRUE, &variant, error))
     goto out;
 
   ot_dump_variant (variant);
index 4622ceef4cbe107a8f1ee1853af0da2dcb9848e5..2ff1c965b4c7abdedf5cca9e987c73cb17394751 100644 (file)
@@ -31,7 +31,7 @@ static char *opt_gpg_homedir;
 
 static GOptionEntry options[] = {
   { "update", 'u', 0, G_OPTION_ARG_NONE, &opt_update, "Update the summary", NULL },
-  { "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "GPG Key ID to sign the commit with", "KEY-ID"},
+  { "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "GPG Key ID to sign the summary with", "KEY-ID"},
   { "gpg-homedir", 0, 0, G_OPTION_ARG_STRING, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR"},
   { NULL }
 };
index 038559bbea35b4e60a876f88faba8c01b1ee5210..811e8924b02366826868e998f03dd687cf659a67 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <libsoup/soup.h>
 
+#include <gio/gunixoutputstream.h>
+
 #include "ot-main.h"
 #include "ot-builtins.h"
 #include "ostree.h"
@@ -32,6 +34,7 @@
 #include <signal.h>
 
 static char *opt_port_file = NULL;
+static char *opt_log = NULL;
 static gboolean opt_daemonize;
 static gboolean opt_autoexit;
 static gboolean opt_force_ranges;
@@ -40,6 +43,7 @@ static gint opt_port = 0;
 typedef struct {
   GFile *root;
   gboolean running;
+  GOutputStream *log;
 } OtTrivialHttpd;
 
 static GOptionEntry options[] = {
@@ -48,9 +52,31 @@ static GOptionEntry options[] = {
   { "port", 'P', 0, G_OPTION_ARG_INT, &opt_port, "Use the specified TCP port", NULL },
   { "port-file", 'p', 0, G_OPTION_ARG_FILENAME, &opt_port_file, "Write port number to PATH (- for standard output)", "PATH" },
   { "force-range-requests", 0, 0, G_OPTION_ARG_NONE, &opt_force_ranges, "Force range requests by only serving half of files", NULL },
+  { "log-file", 0, 0, G_OPTION_ARG_FILENAME, &opt_log, "Put logs here", "PATH" },
   { NULL }
 };
 
+static void
+httpd_log (OtTrivialHttpd *httpd, const gchar *format, ...) __attribute__ ((format(printf, 2, 3)));
+
+static void
+httpd_log (OtTrivialHttpd *httpd, const gchar *format, ...)
+{
+  g_autoptr(GString) str = NULL;
+  va_list args;
+  gsize written;
+
+  if (!httpd->log)
+    return;
+
+  str = g_string_new (NULL);
+  va_start (args, format);
+  g_string_vprintf (str, format, args);
+  va_end (args);
+
+  g_output_stream_write_all (httpd->log, str->str, str->len, &written, NULL, NULL);
+}
+
 static int
 compare_strings (gconstpointer a, gconstpointer b)
 {
@@ -154,6 +180,7 @@ do_get (OtTrivialHttpd    *self,
   struct stat stbuf;
   g_autofree char *safepath = NULL;
 
+  httpd_log (self, "serving %s\n", path);
   if (strstr (path, "../") != NULL)
     {
       soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
@@ -296,6 +323,16 @@ do_get (OtTrivialHttpd    *self,
       soup_message_set_status (msg, SOUP_STATUS_OK);
     }
  out:
+  {
+    guint status = 0;
+    g_autofree gchar *reason = NULL;
+
+    g_object_get (msg,
+                  "status-code", &status,
+                  "reason-phrase", &reason,
+                  NULL);
+    httpd_log (self, "  status: %s (%u)\n", reason, status);
+  }
   return;
 }
 
@@ -305,9 +342,6 @@ httpd_callback (SoupServer *server, SoupMessage *msg,
                 SoupClientContext *context, gpointer data)
 {
   OtTrivialHttpd *self = data;
-  SoupMessageHeadersIter iter;
-
-  soup_message_headers_iter_init (&iter, msg->request_headers);
 
   if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
     do_get (self, server, msg, path, context);
@@ -354,6 +388,37 @@ ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable,
 
   app->root = g_file_new_for_path (dirpath);
 
+  if (opt_log)
+    {
+      GOutputStream *stream = NULL;
+
+      if (g_strcmp0 (opt_log, "-") == 0)
+        {
+          if (opt_daemonize)
+            {
+              ot_util_usage_error (context, "Cannot use --log-file=- and --daemonize at the same time", error);
+              goto out;
+            }
+          stream = G_OUTPUT_STREAM (g_unix_output_stream_new (STDOUT_FILENO, FALSE));
+        }
+      else
+        {
+          g_autoptr(GFile) log_file;
+          GFileOutputStream* log_stream;
+
+          log_file = g_file_new_for_path (opt_log);
+          log_stream = g_file_create (log_file,
+                                      G_FILE_CREATE_PRIVATE,
+                                      cancellable,
+                                      error);
+          if (!log_stream)
+            goto out;
+          stream = G_OUTPUT_STREAM (log_stream);
+        }
+
+      app->log = stream;
+    }
+
 #if SOUP_CHECK_VERSION(2, 48, 0)
   server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "ostree-httpd ", NULL);
   if (!soup_server_listen_all (server, opt_port, 0, error))
@@ -459,13 +524,17 @@ ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable,
         goto out;
       g_signal_connect (dirmon, "changed", G_CALLBACK (on_dir_changed), app);
     }
-
+  {
+    g_autofree gchar *path = g_file_get_path (app->root);
+    httpd_log (app, "serving at root %s\n", path);
+  }
   while (app->running)
     g_main_context_iteration (NULL, TRUE);
+
   ret = TRUE;
  out:
   g_clear_object (&app->root);
+  g_clear_object (&app->log);
   if (context)
     g_option_context_free (context);
   return ret;
index 670ccd6aa30ada4424c905560764f910cbcaf988..48b087af9b2338dd22266303d14174485e006c36 100644 (file)
@@ -118,8 +118,15 @@ dump_commit (GVariant            *variant,
       g_print ("Version: %s\n", version);
     }
 
-  g_print ("\n");
-  dump_indented_lines (subject);
+  if (subject[0])
+    {
+      g_print ("\n");
+      dump_indented_lines (subject);
+    }
+  else
+    {
+      g_print ("(no subject)\n");
+    }
 
   if (body[0])
     {
index 4c29c81e35c6c700dd0dda9620f11670dfb1fc01..66c6e7be3bce296771b4f9e351878abf894dcdd6 100644 (file)
@@ -22,9 +22,9 @@
 
 #include "config.h"
 
+#include "libglnx.h"
 #include "ot-editor.h"
 #include "libgsystem.h"
-#include "libglnx.h"
 
 #include <sys/wait.h>
 #include <string.h>
@@ -101,7 +101,8 @@ ot_editor_prompt (OstreeRepo *repo,
       goto out;
     }
 
-  ret = gs_file_load_contents_utf8 (file, cancellable, error);
+  ret = glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (file), NULL,
+                                        cancellable, error);
 
 out:
   if (file)
index 93f841dcfceab9306ef2479ab7d101ea857ec31e..18c1323960f72ac23658c7cd35ce2a7d0819e5d5 100644 (file)
@@ -27,9 +27,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "ot-main.h"
 #include "ostree.h"
 #include "ot-admin-functions.h"
-#include "ot-main.h"
 #include "otutil.h"
 
 static char *opt_repo;
index 32620c5289006bbfbaa614b59b4e6c69a9a2120c..3bb752438b9e76bdefed60274e1da2675b967b7e 100644 (file)
@@ -22,8 +22,8 @@
 
 #pragma once
 
-#include "ostree.h"
 #include "libglnx.h"
+#include "ostree.h"
 
 typedef enum {
   OSTREE_BUILTIN_FLAG_NONE = 0,
index 5519764a70ee10586a7926b75a822979e8c1b5ca..003df893367bd1bfb0a1c9c53955c45d855d71ef 100755 (executable)
@@ -19,7 +19,7 @@
 
 set -euo pipefail
 
-echo "1..53"
+echo "1..57"
 
 $OSTREE checkout test2 checkout-test2
 echo "ok checkout"
@@ -103,6 +103,12 @@ $OSTREE commit -b test2-no-parent -s '' --parent=none $test_tmpdir/checkout-test
 assert_streq $($OSTREE log test2-no-parent |grep '^commit' | wc -l) "1"
 echo "ok commit no parent"
 
+cd ${test_tmpdir}
+empty_rev=$($OSTREE commit -b test2-no-subject -s '' --timestamp="2005-10-29 12:43:29 +0000" $test_tmpdir/checkout-test2-4)
+omitted_rev=$($OSTREE commit -b test2-no-subject-2 --timestamp="2005-10-29 12:43:29 +0000" $test_tmpdir/checkout-test2-4)
+assert_streq $empty_rev $omitted_rev
+echo "ok commit no subject"
+
 cd ${test_tmpdir}
 $OSTREE commit -b test2-custom-parent -s '' $test_tmpdir/checkout-test2-4
 $OSTREE commit -b test2-custom-parent -s '' $test_tmpdir/checkout-test2-4
@@ -179,13 +185,31 @@ cd ${test_tmpdir}/checkout-test2-4
 $OSTREE commit -b test2 -s "no xattrs" --no-xattrs
 echo "ok commit with no xattrs"
 
+# NB: The + is optional, but we need to make sure we support it
 cd ${test_tmpdir}
 cat > test-statoverride.txt <<EOF
-+2048 /a/nested/3
++1048 /a/nested/2
+2048 /a/nested/3
 EOF
 cd ${test_tmpdir}/checkout-test2-4
-$OSTREE commit -b test2 -s "with statoverride" --statoverride=../test-statoverride.txt
-echo "ok commit statoverridde"
+$OSTREE commit -b test2-override -s "with statoverride" --statoverride=../test-statoverride.txt
+cd ${test_tmpdir}
+$OSTREE checkout test2-override checkout-test2-override
+test -g checkout-test2-override/a/nested/2
+test -u checkout-test2-override/a/nested/3
+echo "ok commit statoverride"
+
+cd ${test_tmpdir}
+cat > test-skiplist.txt <<EOF
+/a/nested/3
+EOF
+cd ${test_tmpdir}/checkout-test2-4
+assert_has_file a/nested/3
+$OSTREE commit -b test2-skiplist -s "with skiplist" --skip-list=../test-skiplist.txt
+cd ${test_tmpdir}
+$OSTREE checkout test2-skiplist checkout-test2-skiplist
+assert_not_has_file checkout-test2-skiplist/a/nested/3
+echo "ok commit skiplist"
 
 cd ${test_tmpdir}
 $OSTREE prune
@@ -254,7 +278,7 @@ echo "ok prune"
 
 cd ${test_tmpdir}
 rm repo3 -rf
-${CMD_PREFIX} ostree --repo=repo3 init --mode=archive-z2
+${CMD_PREFIX} ostree --repo=repo3 init --mode=archive
 ${CMD_PREFIX} ostree --repo=repo3 pull-local --remote=aremote repo test2
 rm repo3/refs/remotes -rf
 mkdir repo3/refs/remotes
@@ -362,9 +386,9 @@ else
     $OSTREE checkout test2 test2-checkout
 fi
 stat '--format=%Y' test2-checkout/baz/cow > cow-mtime
-assert_file_has_content cow-mtime 0
+assert_file_has_content cow-mtime 1
 stat '--format=%Y' test2-checkout/baz/deeper > deeper-mtime
-assert_file_has_content deeper-mtime 0
+assert_file_has_content deeper-mtime 1
 echo "ok content mtime"
 
 cd ${test_tmpdir}
@@ -381,7 +405,7 @@ echo "ok commit of fifo was rejected"
 cd ${test_tmpdir}
 rm repo2 -rf
 mkdir repo2
-${CMD_PREFIX} ostree --repo=repo2 init --mode=archive-z2
+${CMD_PREFIX} ostree --repo=repo2 init --mode=archive
 ${CMD_PREFIX} ostree --repo=repo2 pull-local repo
 rm -rf test2-checkout
 ${CMD_PREFIX} ostree --repo=repo2 checkout -U --disable-cache test2 test2-checkout
@@ -397,6 +421,28 @@ assert_file_has_content test2-checkout/baz/cow moo
 assert_has_dir repo2/uncompressed-objects-cache
 echo "ok disable cache checkout"
 
+cd ${test_tmpdir}
+rm checkout-test2 -rf
+$OSTREE checkout test2 checkout-test2
+date > checkout-test2/date.txt
+rm repo/tmp/* -rf
+export TEST_BOOTID=3072029c-8b10-60d1-d31b-8422eeff9b42
+if env OSTREE_REPO_TEST_ERROR=pre-commit OSTREE_BOOTID=${TEST_BOOTID} \
+       $OSTREE commit -b test2 -s '' $test_tmpdir/checkout-test2 2>err.txt; then
+    assert_not_reached "Should have hit OSTREE_REPO_TEST_ERROR_PRE_COMMIT"
+fi
+assert_file_has_content err.txt OSTREE_REPO_TEST_ERROR_PRE_COMMIT
+found_staging=0
+for d in $(find repo/tmp/ -maxdepth 1 -type d); do
+    bn=$(basename $d)
+    if test ${bn##staging-} != ${bn}; then
+       assert_str_match "${bn}" "^staging-${TEST_BOOTID}-"
+       found_staging=1
+    fi
+done
+assert_streq "${found_staging}" 1
+echo "ok test error pre commit/bootid"
+
 # Whiteouts
 cd ${test_tmpdir}
 mkdir -p overlay/baz/
@@ -468,5 +514,9 @@ cd ..
 if cmp timestamp-{orig,new}.txt; then
     assert_not_reached "failed to update mtime on repo"
 fi
-
 echo "ok mtime updated"
+
+cd ${test_tmpdir}
+$OSTREE init --mode=bare --repo=repo-extensions
+assert_has_dir repo-extensions/extensions
+echo "ok extensions dir"
diff --git a/tests/glib.supp b/tests/glib.supp
new file mode 100644 (file)
index 0000000..3344907
--- /dev/null
@@ -0,0 +1,535 @@
+# This GLib suppressions file is known to be used at least by:
+#
+#  - rpm-software-management/libhif
+#
+# Please use the upstream verison in libhif for changes.
+{
+   gobject_init_1
+   Memcheck:Leak
+   ...
+   fun:gobject_init
+}
+{
+   g_type_register_static_1
+   Memcheck:Leak
+   ...
+   fun:g_type_register_static
+}
+{
+   g_type_register_fundamental
+   Memcheck:Leak
+   ...
+   fun:g_type_register_fundamental
+}
+{
+   g_type_init_with_debug_flags
+   Memcheck:Leak
+   ...
+   fun:g_type_init_with_debug_flags
+}
+{
+   g_type_class_ref_1
+   Memcheck:Leak
+   ...
+   fun:type_iface_vtable_base_init_Wm
+   ...
+   fun:g_type_class_ref
+}
+{
+   g_type_class_ref_2
+   Memcheck:Leak
+   ...
+   fun:type_class_init_Wm
+   ...
+   fun:g_type_class_ref
+}
+{
+   g_type_add_interface_static
+   Memcheck:Leak
+   ...
+   fun:g_type_add_interface_static
+}
+{
+   g_param_spec_internal
+   Memcheck:Leak
+   ...
+   fun:g_type_class_ref
+   fun:g_type_create_instance
+   fun:g_param_spec_internal
+}
+{
+   g_param_spec_enum
+   Memcheck:Leak
+   ...
+   fun:g_type_class_ref
+   fun:g_param_spec_enum
+}
+{
+   g_param_spec_flags
+   Memcheck:Leak
+   ...
+   fun:g_type_class_ref
+   fun:g_param_spec_flags
+}
+{
+   g_quark_from_static_string
+   Memcheck:Leak
+   ...
+   fun:g_quark_from_static_string
+}
+{
+   g_quark_from_string
+   Memcheck:Leak
+   ...
+   fun:g_quark_from_string
+}
+{
+   g_value_register_transform_func
+   Memcheck:Leak
+   ...
+   fun:g_value_register_transform_func
+}
+{
+   test_run_seed
+   Memcheck:Leak
+   ...
+   fun:g_rand_new_with_seed_array
+   fun:test_run_seed
+   ...
+   fun:g_test_run_suite
+}
+{
+   g_test_init
+   Memcheck:Leak
+   ...
+   fun:g_rand_new_with_seed_array
+   ...
+   fun:g_test_init
+}
+{
+   g_intern_static_string
+   Memcheck:Leak
+   ...
+   fun:g_intern_static_string
+}
+{
+   g_main_context_push_thread_default
+   Memcheck:Leak
+   ...
+   fun:g_queue_new
+   fun:g_main_context_push_thread_default
+}
+{
+   g_main_context_push_thread_default_inlined
+   Memcheck:Leak
+   ...
+   fun:g_slice_alloc0
+   fun:g_main_context_push_thread_default
+}
+{
+   g_dbus_error_register_error
+   Memcheck:Leak
+   ...
+   fun:g_dbus_error_register_error
+}
+{
+   g_param_spec_pool_insert
+   Memcheck:Leak
+   ...
+   fun:g_param_spec_pool_insert
+}
+{
+   g_main_context_default
+   Memcheck:Leak
+   ...
+   fun:g_main_context_default
+}
+{
+   g_main_context_check
+   Memcheck:Leak
+   ...
+   fun:g_ptr_array_add
+   fun:g_main_context_check
+}
+{
+   g_test_run_suite
+   Memcheck:Leak
+   ...
+   fun:g_slist_copy
+   fun:g_test_run_suite_internal
+   fun:g_test_run_suite
+}
+{
+   g_dbus_interface_info_cache_build
+   Memcheck:Leak
+   ...
+   fun:g_dbus_interface_info_cache_build
+}
+{
+   g_cancellable_push_current
+   Memcheck:Leak
+   ...
+   fun:thread_memory_from_self
+   ...
+   fun:g_cancellable_push_current
+}
+{
+   _g_io_module_get_default
+   Memcheck:Leak
+   ...
+   fun:g_io_module_new
+   fun:g_io_modules_scan_all_in_directory_with_scope
+   fun:_g_io_modules_ensure_loaded
+   fun:_g_io_module_get_default
+}
+{
+   g_io_scheduler_push_job
+   Memcheck:Leak
+   ...
+   fun:init_scheduler
+   fun:g_once_impl
+   fun:g_io_scheduler_push_job
+}
+{
+   g_io_scheduler_push_job_2
+   Memcheck:Leak
+   ...
+   fun:g_system_thread_new
+   ...
+   fun:g_io_scheduler_push_job
+}
+{
+   g_bus_get_sync__available_connections
+   Memcheck:Leak
+   ...
+   fun:g_hash_table_new
+   fun:initable_init
+   fun:g_initable_init
+   fun:g_bus_get_sync
+}
+{
+   g_socket_connection_factory_register_type
+   Memcheck:Leak
+   ...
+   fun:g_socket_connection_factory_register_type
+}
+{
+   g_test_add_vtable
+   Memcheck:Leak
+   ...
+   fun:g_test_add_vtable
+}
+{
+   g_mutex_lock
+   Memcheck:Leak
+   ...
+   fun:g_mutex_impl_new
+   fun:g_mutex_get_impl
+   fun:g_mutex_lock
+}
+{
+   g_thread_self
+   Memcheck:Leak
+   ...
+   fun:g_thread_self
+}
+{
+   g_rec_mutex_lock
+   Memcheck:Leak
+   ...
+   fun:g_rec_mutex_impl_new
+   fun:g_rec_mutex_get_impl
+   fun:g_rec_mutex_lock
+}
+{
+   test_case_run
+   Memcheck:Leak
+   ...
+   fun:g_malloc0
+   fun:test_case_run
+   ...
+   fun:g_test_run_suite
+}
+{
+   g_get_charset
+   Memcheck:Leak
+   ...
+   fun:g_get_charset
+}
+{
+   g_test_run_suite__timer_new
+   Memcheck:Leak
+   ...
+   fun:g_timer_new
+   fun:test_case_run
+   ...
+   fun:g_test_run_suite
+}
+{
+   g_test_run_suite__timer_new2
+   Memcheck:Leak
+   ...
+   fun:g_timer_new
+   fun:test_case_run_suite_internal
+   ...
+   fun:g_test_run_suite
+}
+{
+   g_test_run_suite__strconcat
+   Memcheck:Leak
+   ...
+   fun:g_strconcat
+   fun:test_case_run
+   ...
+   fun:g_test_run_suite
+   fun:g_test_run
+}
+{
+   g_type_interface_add_prerequisite
+   Memcheck:Leak
+   ...
+   fun:g_type_interface_add_prerequisite
+}
+{
+   <insert_a_suppression_name_here>
+   Memcheck:Leak
+   ...
+   fun:g_slist_copy
+   fun:g_test_run_suite_internal
+   ...
+   fun:g_test_run_suite
+}
+{
+   g_set_prgname
+   Memcheck:Leak
+   ...
+   fun:g_set_prgname
+}
+{
+   g_test_run_suite__strconcat_2
+   Memcheck:Leak
+   ...
+   fun:g_strconcat
+   fun:g_test_run_suite_internal
+}
+{
+   g_test_run_suite__strdup
+   Memcheck:Leak
+   ...
+   fun:g_strdup
+   fun:g_test_run_suite_internal
+}
+{
+   g_private_get
+   Memcheck:Leak
+   ...
+   fun:g_private_get
+}
+{
+   g_private_set
+   Memcheck:Leak
+   ...
+   fun:g_private_set
+}
+{
+   g_static_mutex_get_mutex_impl
+   Memcheck:Leak
+   ...
+   fun:g_static_mutex_get_mutex_impl
+}
+{
+   g_variant_type_info_unref
+   Memcheck:Leak
+   ...
+   fun:g_hash_table_remove
+   fun:g_variant_type_info_unref
+}
+{
+   g_rw_lock_reader_lock
+   Memcheck:Leak
+   ...
+   fun:g_rw_lock_impl_new
+   fun:g_rw_lock_get_impl
+   fun:g_rw_lock_reader_lock
+}
+{
+   g_child_watch_finalize__rt_sigaction
+   Memcheck:Param
+   rt_sigaction(act->sa_flags)
+   fun:__libc_sigaction
+   ...
+   fun:g_child_watch_finalize
+}
+{
+   g_dbus_worker_new
+   Memcheck:Leak
+   fun:calloc
+   ...
+   fun:_g_dbus_worker_new
+}
+{
+   gdbus_shared_thread_func
+   Memcheck:Leak
+   match-leak-kinds: definite
+   ...
+   fun:g_malloc
+   ...
+   fun:gdbus_shared_thread_func
+}
+{
+   g_task_start_task_thread
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   fun:g_malloc
+   fun:g_slice_alloc
+   fun:g_slice_alloc0
+   ...
+   fun:g_thread_pool_push
+   fun:g_task_start_task_thread
+}
+{
+   g_get_language_names
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:calloc
+   fun:g_malloc0
+   fun:g_get_language_names
+}
+{
+   g_get_filename_charsets
+   Memcheck:Leak
+   match-leak-kinds: definite
+   ...
+   fun:g_get_filename_charsets
+   fun:g_filename_display_name
+}
+{
+   g_main_current_source
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   fun:g_malloc
+   ...
+   fun:g_main_current_source
+   fun:g_task_return
+   fun:g_task_thread_pool_thread
+}
+{
+   g_once_init_enter
+   Memcheck:Leak
+   match-leak-kinds: definite
+   ...
+   fun:g_once_init_enter
+}
+{
+   g_child_watch_source_new
+   Memcheck:Leak
+   match-leak-kinds: definite
+   ...
+   fun:g_thread_new
+   ...
+   fun:g_child_watch_source_new
+}
+{
+   continue_writing_in_idle_cb
+   Memcheck:Leak
+   match-leak-kinds: definite
+   ...
+   fun:g_task_new
+   ...
+   fun:continue_writing_in_idle_cb
+   fun:g_main_context_dispatch
+}
+{
+   g_main_current_source
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   ...
+   fun:g_main_current_source
+}
+{
+   g_thread_pool_push
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   ...
+   fun:g_thread_pool_push
+}
+{
+   leak_test_dbus_dispose
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   ...
+   fun:g_main_loop_run
+   fun:g_test_dbus_down
+}
+{
+   leak_test_dbus_down
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:calloc
+   fun:g_malloc0
+   fun:g_main_loop_new
+   fun:g_test_dbus_down
+}
+{
+   leak_socket_client_connect
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   fun:g_malloc
+   fun:g_slice_alloc
+   fun:g_slice_alloc0
+   fun:g_socket_client_connect_async
+   fun:g_socket_client_connect_to_uri_async
+}
+{
+   leak_signal_handlers_disconnect_matched
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:calloc
+   fun:g_malloc0
+   ...
+   fun:g_slice_alloc
+   ...
+   fun:g_signal_handlers_disconnect_matched
+}
+{
+   g_tls_connection_gnutls_init_priorities
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   fun:g_malloc
+   fun:g_strdup
+   fun:g_tls_connection_gnutls_init_priorities
+}
+{
+   g_tls_connection_gnutls_heisenbug_likely_same_as_above
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   fun:g_malloc
+   fun:g_strdup
+   ...
+   fun:g_tls_client_connection_new
+}
+{
+   g_unix_signal_add_full
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   fun:g_malloc
+   ...
+   fun:g_thread_new
+   ...
+   fun:g_unix_signal_add_full
+}
+{
+   glib_worker_1
+   Memcheck:Leak
+   ...
+   fun:glib_worker_main
+}
index 58283368ae1212019543ee82de66a79a8007bae4..81f743e9e23d299c2117953e16409fbcd2af0293 100644 (file)
 /* This function hovers in a quantum superposition of horrifying and
  * beautiful.  Future generations may interpret it as modern art.
  */
-static gboolean
-run_libtest (const char *cmd, GError **error)
+gboolean
+ot_test_run_libtest (const char *cmd, GError **error)
 {
   gboolean ret = FALSE;
-  const char *builddir = g_getenv ("G_TEST_BUILDDIR");
+  const char *srcdir = g_getenv ("G_TEST_SRCDIR");
   int estatus;
   g_autoptr(GPtrArray) argv = g_ptr_array_new ();
   g_autoptr(GString) cmdstr = g_string_new ("");
@@ -40,8 +40,8 @@ run_libtest (const char *cmd, GError **error)
   g_ptr_array_add (argv, "bash");
   g_ptr_array_add (argv, "-c");
 
-  g_string_append (cmdstr, ". ");
-  g_string_append (cmdstr, builddir);
+  g_string_append (cmdstr, "set -xeuo pipefail; . ");
+  g_string_append (cmdstr, srcdir);
   g_string_append (cmdstr, "/tests/libtest.sh; ");
   g_string_append (cmdstr, cmd);
 
@@ -68,7 +68,7 @@ ot_test_setup_repo (GCancellable *cancellable,
   g_autoptr(GFile) repo_path = g_file_new_for_path ("repo");
   glnx_unref_object OstreeRepo* ret_repo = NULL;
 
-  if (!run_libtest ("setup_test_repository", error))
+  if (!ot_test_run_libtest ("setup_test_repository archive-z2", error))
     goto out;
 
   ret_repo = ostree_repo_new (repo_path);
@@ -91,7 +91,7 @@ ot_test_setup_sysroot (GCancellable *cancellable,
   g_autoptr(GFile) sysroot_path = g_file_new_for_path ("sysroot");
   glnx_unref_object OstreeSysroot *ret_sysroot = NULL;
 
-  if (!run_libtest ("setup_os_repository \"archive-z2\" \"syslinux\"", error))
+  if (!ot_test_run_libtest ("setup_os_repository \"archive-z2\" \"syslinux\"", error))
     goto out;
 
   ret_sysroot = ostree_sysroot_new (sysroot_path);
index eb9bb0b2abbcf593fa7cc62043887c95d5ed7e40..f777545eace559a543efec30899fa18d22c0cc5c 100644 (file)
@@ -27,6 +27,7 @@
 
 G_BEGIN_DECLS
 
+gboolean ot_test_run_libtest (const char *cmd, GError **error);
 
 OstreeRepo *ot_test_setup_repo (GCancellable *cancellable,
                                 GError **error);
index 572f023c32b204b28a6664e9e463501d56872ea9..2d064299cb93b5b8e09cce93b6084d7f71a60815 100755 (executable)
@@ -69,6 +69,7 @@ export TEST_GPG_KEYID_3="DF444D67"
 # this by copying locally.
 echo "Copying gpghome to ${test_tmpdir}"
 cp -a "${test_srcdir}/gpghome" ${test_tmpdir}
+chmod -R u+w "${test_tmpdir}"
 export TEST_GPG_KEYHOME=${test_tmpdir}/gpghome
 export OSTREE_GPG_HOME=${test_tmpdir}/gpghome/trusted
 
@@ -77,15 +78,28 @@ if test -n "${OT_TESTS_DEBUG:-}"; then
 fi
 
 if test -n "${OT_TESTS_VALGRIND:-}"; then
-    CMD_PREFIX="env G_SLICE=always-malloc valgrind -q --leak-check=full --num-callers=30 --suppressions=${test_srcdir}/ostree-valgrind.supp"
+    CMD_PREFIX="env G_SLICE=always-malloc OSTREE_SUPPRESS_SYNCFS=1 valgrind -q --error-exitcode=1 --leak-check=full --num-callers=30 --suppressions=${test_srcdir}/glib.supp --suppressions=${test_srcdir}/ostree.supp"
 else
-    CMD_PREFIX="env LD_PRELOAD=${test_builddir}/libreaddir-rand.so"
+    # In some cases the LD_PRELOAD may cause obscure problems,
+    # e.g. right now it breaks for me with -fsanitize=address, so
+    # let's allow users to skip it.
+    if test -z "${OT_SKIP_READDIR_RAND:-}"; then
+       CMD_PREFIX="env LD_PRELOAD=${test_builddir}/libreaddir-rand.so"
+    else
+       CMD_PREFIX=""
+    fi
 fi
 
 assert_streq () {
     test "$1" = "$2" || (echo 1>&2 "$1 != $2"; exit 1)
 }
 
+assert_str_match () {
+    if ! echo "$1" | grep -E -q "$2"; then
+       (echo 1>&2 "$1 does not match regexp $2"; exit 1)
+    fi
+}
+
 assert_not_streq () {
     (! test "$1" = "$2") || (echo 1>&2 "$1 == $2"; exit 1)
 }
@@ -100,13 +114,17 @@ assert_has_dir () {
 
 assert_not_has_file () {
     if test -f "$1"; then
-       echo 1>&2 "File '$1' exists"; exit 1
+        sed -e 's/^/# /' < "$1" >&2
+        echo 1>&2 "File '$1' exists"
+        exit 1
     fi
 }
 
 assert_not_file_has_content () {
     if grep -q -e "$2" "$1"; then
-       echo 1>&2 "File '$1' incorrectly matches regexp '$2'"; exit 1
+        sed -e 's/^/# /' < "$1" >&2
+        echo 1>&2 "File '$1' incorrectly matches regexp '$2'"
+        exit 1
     fi
 }
 
@@ -118,13 +136,38 @@ assert_not_has_dir () {
 
 assert_file_has_content () {
     if ! grep -q -e "$2" "$1"; then
-       echo 1>&2 "File '$1' doesn't match regexp '$2'"; exit 1
+        sed -e 's/^/# /' < "$1" >&2
+        echo 1>&2 "File '$1' doesn't match regexp '$2'"
+        exit 1
+    fi
+}
+
+assert_symlink_has_content () {
+    if ! test -L "$1"; then
+        echo 1>&2 "File '$1' is not a symbolic link"
+        exit 1
+    fi
+    if ! readlink "$1" | grep -q -e "$2"; then
+        sed -e 's/^/# /' < "$1" >&2
+        echo 1>&2 "Symbolic link '$1' doesn't match regexp '$2'"
+        exit 1
     fi
 }
 
 assert_file_empty() {
     if test -s "$1"; then
-       echo 1>&2 "File '$1' is not empty"; exit 1
+        sed -e 's/^/# /' < "$1" >&2
+        echo 1>&2 "File '$1' is not empty"
+        exit 1
+    fi
+}
+
+assert_files_hardlinked() {
+    f1=$(stat -c %i $1)
+    f2=$(stat -c %i $2)
+    if [ "$f1" != "$f2" ]; then
+        echo 1>&2 "Files '$1' and '$2' are not hardlinked"
+        exit 1
     fi
 }
 
@@ -370,6 +413,11 @@ skip_without_fuse () {
         exit 0
     fi
 
+    if ! capsh --print | grep -q 'Bounding set.*[^a-z]cap_sys_admin'; then
+       echo "1..0 # SKIP No cap_sys_admin in bounding set, can't use FUSE"
+        exit 0
+    fi
+
     if ! [ -w /dev/fuse ]; then
         echo "1..0 # SKIP no write access to /dev/fuse"
         exit 0
diff --git a/tests/ostree-valgrind.supp b/tests/ostree-valgrind.supp
deleted file mode 100644 (file)
index 00efd87..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-{
-   g_type_init_with_debug_flags calloc
-   Memcheck:Leak
-   fun:calloc
-   ...
-   fun:g_type_init_with_debug_flags
-   ...
-}
-
-{
-  g_type_add_interface_static malloc
-  Memcheck:Leak
-  fun:malloc
-  ...
-  fun:g_type_add_interface_static
-  ...
-}
-
-{
-  g_type_add_interface_dynamic malloc
-  Memcheck:Leak
-  fun:malloc
-  ...
-  fun:g_type_add_interface_dynamic
-  ...
-}
-
-{
-  g_type_class_ref malloc
-  Memcheck:Leak
-  fun:malloc
-  ...
-  fun:g_type_class_ref
-  ...
-}
-
-{
-  g_type_register_dynamic malloc
-  Memcheck:Leak
-  fun:malloc
-  ...
-  fun:g_type_register_dynamic
-  ...
-}
-
-{
-   g_type_init_with_debug_flags malloc
-   Memcheck:Leak
-   fun:malloc
-   ...
-   fun:g_type_init_with_debug_flags
-   ...
-}
-
-{
-   g_type_init_with_debug_flags realloc
-   Memcheck:Leak
-   fun:realloc
-   ...
-   fun:g_type_init_with_debug_flags
-   ...
-}
-
-{
-   g_test_add_vtable malloc
-   Memcheck:Leak
-   fun:malloc
-   ...
-   fun:g_test_add_vtable
-   ...
-}
-
-{
-   g_test_init
-   Memcheck:Leak
-   fun:malloc
-   ...
-   fun:g_test_init
-   ...
-}
-
-{
-   g_type_register_static malloc
-   Memcheck:Leak
-   fun:malloc
-   ...
-   fun:g_type_register_static
-   ...
-}
-
-{
-   g_type_register_static realloc
-   Memcheck:Leak
-   fun:realloc
-   ...
-   fun:g_type_register_static
-   ...
-}
-
-{
-   g_type_register_fundamental never freed
-   Memcheck:Leak
-   fun:malloc
-   ...
-   fun:g_type_register_fundamental
-   ...
-}
-
-{
-   g_type_class_ref never finalized
-   Memcheck:Leak
-   fun:calloc
-   ...
-   fun:g_type_class_ref
-   ...
-}
-
-{
-   DBusGValue qdata
-   Memcheck:Leak
-   fun:realloc
-   fun:g_realloc
-   fun:g_type_set_qdata
-   fun:_dbus_g_value_types_init
-   ...
-}
-
-{
-   gettext conditional jump
-   Memcheck:Cond
-   fun:__GI___strcasecmp_l
-   fun:__gconv_open
-   fun:_nl_find_msg
-   fun:__dcigettext
-   ...
-}
-
-{
-   gettext uninitialized value
-   Memcheck:Value8
-   fun:__GI___strcasecmp_l
-   fun:__gconv_open
-   fun:_nl_find_msg
-   fun:__dcigettext
-   ...
-}
-
-{
-   font config invalid reads
-   Memcheck:Addr4
-   ...
-   fun:FcConfigParseAndLoad
-   ...
-}
-
-{
-   dynamic loader conditional jump
-   Memcheck:Cond
-   fun:index
-   fun:expand_dynamic_string_token
-   fun:_dl_map_object
-   fun:map_doit
-   fun:_dl_catch_error
-   fun:do_preload
-   fun:dl_main
-   ...
-}
-
-{
-   g_vfs_get_local
-   Memcheck:Leak
-   ...
-   fun:g_vfs_get_local
-   ...
-}
-
-{
-   _g_io_modules_ensure_loaded
-   Memcheck:Leak
-   ...
-   fun:_g_io_modules_ensure_loaded
-   ...
-}
-
-{
-   _g_io_module_get_default
-   Memcheck:Leak
-   ...
-   fun:_g_io_module_get_default
-   ...
-}
-
-{
-   _dl_allocate_tls
-   Memcheck:Leak
-   ...
-   fun:_dl_allocate_tls
-   ...
-}
diff --git a/tests/ostree.supp b/tests/ostree.supp
new file mode 100644 (file)
index 0000000..b81ea51
--- /dev/null
@@ -0,0 +1 @@
+# Use this to suppress "possibly lost" for global statics
index 527d4a3a0945b2757f25be4bc12b500263672ce3..afef387c85c6214ba836743a9341423d39f839f3 100644 (file)
@@ -97,7 +97,6 @@ readdir (DIR *dirp)
   while (cache_another)
     {
       DirEntries *de;
-      GSList *l;
 
       errno = 0;
       ret = real_readdir (dirp);
@@ -110,12 +109,12 @@ readdir (DIR *dirp)
        {
          if (g_random_boolean ())
            {
+             struct dirent *copy;
              if (!de)
                {
                  de = dir_entries_new ();
                  g_hash_table_insert (direntcache, dirp, de);
                }
-             struct dirent *copy;
              copy = g_memdup (ret, sizeof (struct dirent));
              g_ptr_array_add (de->entries, copy);
            }
diff --git a/tests/test-abi.sh b/tests/test-abi.sh
deleted file mode 100755 (executable)
index 62af365..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 Colin Walters <walters@verbum.org>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-set -euo pipefail
-
-echo '1..1'
-
-grep ' ostree_[A-Za-z0-9_]*;' ${G_TEST_SRCDIR}/src/libostree/libostree.sym | sed -e 's,^ *\([A-Za-z0-9_]*\);,\1,' | sort -u > expected-symbols.txt
-eu-readelf -a ${G_TEST_BUILDDIR}/.libs/libostree-1.so | grep 'FUNC.*GLOBAL.*DEFAULT.*@@LIBOSTREE_' | sed -e 's,^.* \(ostree_[A-Za-z0-9_]*\)@@LIBOSTREE_[0-9_.]*,\1,' |sort -u > found-symbols.txt
-diff -u expected-symbols.txt found-symbols.txt
-
-echo 'ok'
diff --git a/tests/test-admin-deploy-bootid-gc.sh b/tests/test-admin-deploy-bootid-gc.sh
new file mode 100755 (executable)
index 0000000..ba16f33
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 Colin Walters <walters@verbum.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+set -euo pipefail
+
+. $(dirname $0)/libtest.sh
+
+# Exports OSTREE_SYSROOT so --sysroot not needed.
+setup_os_repository "archive-z2" "syslinux"
+
+echo "1..1"
+
+${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime
+rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime)
+export rev
+${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime
+os_repository_new_commit
+
+rm sysroot/ostree/repo/tmp/* -rf
+export TEST_BOOTID=4072029c-8b10-60d1-d31b-8422eeff9b42
+if env OSTREE_REPO_TEST_ERROR=pre-commit OSTREE_BOOTID=${TEST_BOOTID} \
+       ${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime 2>err.txt; then
+    assert_not_reached "Should have hit OSTREE_REPO_TEST_ERROR_PRE_COMMIT"
+fi
+stagepath=$(ls -d sysroot/ostree/repo/tmp/staging-${TEST_BOOTID}-*)
+assert_has_dir "${stagepath}"
+
+# We have an older failed stage, now use a new boot id
+
+export NEW_TEST_BOOTID=5072029c-8b10-60d1-d31b-8422eeff9b42
+if env OSTREE_REPO_TEST_ERROR=pre-commit OSTREE_BOOTID=${NEW_TEST_BOOTID} \
+       ${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=FOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime 2>err.txt; then
+    assert_not_reached "Should have hit OSTREE_REPO_TEST_ERROR_PRE_COMMIT"
+fi
+newstagepath=$(ls -d sysroot/ostree/repo/tmp/staging-${NEW_TEST_BOOTID}-*)
+assert_has_dir "${newstagepath}"
+env OSTREE_BOOTID=${NEW_TEST_BOOTID} ${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime
+newstagepath=$(ls -d sysroot/ostree/repo/tmp/staging-${NEW_TEST_BOOTID}-*)
+assert_not_has_dir "${stagepath}"
+assert_not_has_dir "${newstagepath}"
+
+echo "ok admin bootid GC"
index d5dcc81176e8be9546e589d326df37ba0e4deb89..447c46ea54a18a4f05a950b6e4a1e406f80dd2fd 100644 (file)
@@ -34,6 +34,143 @@ test_repo_is_not_system (gconstpointer data)
   g_assert (!ostree_repo_is_system (repo));
 }
 
+static GBytes *
+input_stream_to_bytes (GInputStream *input)
+{
+  g_autoptr(GOutputStream) mem_out_stream = NULL;
+  g_autoptr(GError) error = NULL;
+
+  if (input == NULL)
+    return g_bytes_new (NULL, 0);
+
+  mem_out_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+  g_output_stream_splice (mem_out_stream,
+                          input,
+                          G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+                          NULL,
+                          &error);
+  g_assert_no_error (error);
+
+  return g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (mem_out_stream));
+}
+
+static void
+test_raw_file_to_archive_z2_stream (gconstpointer data)
+{
+  OstreeRepo *repo = OSTREE_REPO (data);
+  g_autofree gchar *commit_checksum = NULL;
+  g_autoptr(GHashTable) reachable = NULL;
+  g_autoptr(GError) error = NULL;
+  /* branch name of the test repository, see setup_test_repository in libtest.sh */
+  const gchar *rev = "test2";
+  GHashTableIter iter;
+  GVariant *serialized_object;
+  guint checks = 0;
+
+  ostree_repo_resolve_rev (repo,
+                           rev,
+                           FALSE,
+                           &commit_checksum,
+                           &error);
+  g_assert_no_error (error);
+  reachable = ostree_repo_traverse_new_reachable ();
+  ostree_repo_traverse_commit (repo,
+                               commit_checksum,
+                               -1,
+                               &reachable,
+                               NULL,
+                               &error);
+  g_assert_no_error (error);
+  g_hash_table_iter_init (&iter, reachable);
+  while (g_hash_table_iter_next (&iter, (gpointer*)&serialized_object, NULL))
+    {
+      const gchar *object_checksum;
+      OstreeObjectType object_type;
+      g_autoptr(GInputStream) input = NULL;
+      g_autoptr(GFileInfo) info = NULL;
+      g_autoptr(GVariant) xattrs = NULL;
+      g_autoptr(GBytes) input_bytes = NULL;
+      g_autoptr(GInputStream) mem_input = NULL;
+      g_autoptr(GInputStream) zlib_stream = NULL;
+      g_autoptr(GBytes) zlib_bytes = NULL;
+      g_autoptr(GInputStream) mem_zlib = NULL;
+      g_autoptr(GInputStream) input2 = NULL;
+      g_autoptr(GFileInfo) info2 = NULL;
+      g_autoptr(GVariant) xattrs2 = NULL;
+      g_autoptr(GBytes) input2_bytes = NULL;
+
+      ostree_object_name_deserialize (serialized_object, &object_checksum, &object_type);
+      if (object_type != OSTREE_OBJECT_TYPE_FILE)
+        continue;
+
+      ostree_repo_load_file (repo,
+                             object_checksum,
+                             &input,
+                             &info,
+                             &xattrs,
+                             NULL,
+                             &error);
+      g_assert_no_error (error);
+
+      input_bytes = input_stream_to_bytes (input);
+      /* This is to simulate NULL input received from
+       * ostree_repo_load_file. Instead of creating the mem_input
+       * variable, I could also rewind the input stream and pass it to
+       * the function below, but this would assume that the input
+       * stream implements either the GSeekable or
+       * GFileDescriptorBased interface. */
+      if (input != NULL)
+        mem_input = g_memory_input_stream_new_from_bytes (input_bytes);
+      ostree_raw_file_to_archive_z2_stream (mem_input,
+                                            info,
+                                            xattrs,
+                                            &zlib_stream,
+                                            NULL,
+                                            &error);
+      g_assert_no_error (error);
+
+      zlib_bytes = input_stream_to_bytes (zlib_stream);
+      mem_zlib = g_memory_input_stream_new_from_bytes (zlib_bytes);
+      ostree_content_stream_parse (FALSE,
+                                   mem_zlib,
+                                   g_bytes_get_size (zlib_bytes),
+                                   FALSE,
+                                   &input2,
+                                   &info2,
+                                   &xattrs2,
+                                   NULL,
+                                   &error);
+      g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+      g_clear_error (&error);
+
+      g_seekable_seek (G_SEEKABLE (mem_zlib),
+                       0,
+                       G_SEEK_SET,
+                       NULL,
+                       &error);
+      g_assert_no_error (error);
+
+      ostree_content_stream_parse (TRUE,
+                                   mem_zlib,
+                                   g_bytes_get_size (zlib_bytes),
+                                   FALSE,
+                                   &input2,
+                                   &info2,
+                                   &xattrs2,
+                                   NULL,
+                                   &error);
+      g_assert_no_error (error);
+
+      input2_bytes = input_stream_to_bytes (input2);
+      g_assert_true (g_bytes_equal (input_bytes, input2_bytes));
+      g_assert_true (g_variant_equal (xattrs, xattrs2));
+      /* TODO: Not sure how to compare fileinfos */
+      ++checks;
+    }
+  /* to make sure we really tested the function */
+  g_assert_cmpint (checks, >, 0);
+}
+
 int main (int argc, char **argv)
 {
   g_autoptr(GError) error = NULL;
@@ -46,6 +183,7 @@ int main (int argc, char **argv)
     goto out;
   
   g_test_add_data_func ("/repo-not-system", repo, test_repo_is_not_system);
+  g_test_add_data_func ("/raw-file-to-archive-z2-stream", repo, test_raw_file_to_archive_z2_stream);
 
   return g_test_run();
  out:
index 25d3c37ea66775fb3271341e8c0eec834dc94772..e7fd7be46447d5385ead79838804c5dc83211de4 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "config.h"
 
+#include "libglnx.h"
 #include "libgsystem.h"
 #include <glib.h>
 #include <stdlib.h>
index 1e81a2e97f4dfaf77e4ccbd3a889bbf5d722ce08..856c407361c474ffbb354253691a07b559b76fec 100755 (executable)
@@ -23,7 +23,7 @@ set -euo pipefail
 
 setup_test_repository "archive-z2"
 
-echo '1..2'
+echo '1..5'
 
 $OSTREE checkout test2 test2-co
 $OSTREE commit --no-xattrs -b test2-noxattrs -s "test2 without xattrs" --tree=dir=test2-co
@@ -35,10 +35,39 @@ mkdir t
 (cd t && tar xf ../test2.tar)
 ${CMD_PREFIX} ostree --repo=repo diff --no-xattrs test2-noxattrs ./t > diff.txt
 assert_file_empty diff.txt
-rm test2.tar diff.txt t -rf
 
 echo 'ok export gnutar diff (no xattrs)'
 
+cd ${test_tmpdir}
+${OSTREE} 'export' test2-noxattrs --subpath=baz -o test2-subpath.tar
+mkdir t2
+(cd t2 && tar xf ../test2-subpath.tar)
+${CMD_PREFIX} ostree --repo=repo diff --no-xattrs ./t2 ./t/baz > diff.txt
+assert_file_empty diff.txt
+
+echo 'ok export --subpath gnutar diff (no xattrs)'
+
+cd ${test_tmpdir}
+${OSTREE} 'export' test2-noxattrs --prefix=the-prefix/ -o test2-prefix.tar
+mkdir t3
+(cd t3 && tar xf ../test2-prefix.tar)
+${CMD_PREFIX} ostree --repo=repo diff --no-xattrs test2-noxattrs ./t3/the-prefix > diff.txt
+assert_file_empty diff.txt
+
+echo 'ok export --prefix gnutar diff (no xattrs)'
+
+cd ${test_tmpdir}
+${OSTREE} 'export' test2-noxattrs --subpath=baz --prefix=the-prefix/ -o test2-prefix-subpath.tar
+mkdir t4
+(cd t4 && tar xf ../test2-prefix-subpath.tar)
+${CMD_PREFIX} ostree --repo=repo diff --no-xattrs ./t4/the-prefix ./t/baz > diff.txt
+${CMD_PREFIX} ostree --repo=repo diff --no-xattrs test2-noxattrs ./t3/the-prefix > diff.txt
+assert_file_empty diff.txt
+
+echo 'ok export --prefix --subpath gnutar diff (no xattrs)'
+
+rm test2.tar test2-subpath.tar diff.txt t t2 t3 t4 -rf
+
 cd ${test_tmpdir}
 ${OSTREE} 'export' test2 -o test2.tar
 ${OSTREE} commit -b test2-from-tar -s 'Import from tar' --tree=tar=test2.tar
index 877fa77cf9e868bbefba6c8f38b3056c5b5add29..aaa3c37b2ce650d3fc5b766e1436949e38165a78 100644 (file)
@@ -42,6 +42,8 @@ test_data_init (TestData *td)
   GError *error = NULL;
   struct archive *a = archive_write_new ();
   struct archive_entry *ae;
+  uid_t uid = getuid ();
+  gid_t gid = getgid ();
 
   td->tmpd = g_mkdtemp (g_strdup ("/var/tmp/test-libarchive-import-XXXXXX"));
   g_assert_cmpint (0, ==, chdir (td->tmpd));
@@ -59,12 +61,16 @@ test_data_init (TestData *td)
   ae = archive_entry_new ();
   archive_entry_set_pathname (ae, "/");
   archive_entry_set_mode (ae, S_IFDIR | 0755);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
   g_assert_cmpint (0, ==, archive_write_header (a, ae));
   archive_entry_free (ae);
 
   ae = archive_entry_new ();
   archive_entry_set_pathname (ae, "/file");
   archive_entry_set_mode (ae, S_IFREG | 0777);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
   archive_entry_set_size (ae, 4);
   g_assert_cmpint (0, ==, archive_write_header (a, ae));
   g_assert_cmpint (4, ==, archive_write_data (a, "foo\n", 4));
@@ -73,6 +79,8 @@ test_data_init (TestData *td)
   ae = archive_entry_new ();
   archive_entry_set_pathname (ae, "/devnull");
   archive_entry_set_mode (ae, S_IFCHR | 0777);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
   archive_entry_set_devmajor (ae, 1);
   archive_entry_set_devminor (ae, 3);
   g_assert_cmpint (0, ==, archive_write_header (a, ae));
@@ -81,6 +89,26 @@ test_data_init (TestData *td)
   ae = archive_entry_new ();
   archive_entry_set_pathname (ae, "/anotherfile");
   archive_entry_set_mode (ae, S_IFREG | 0777);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
+  archive_entry_set_size (ae, 4);
+  g_assert_cmpint (0, ==, archive_write_header (a, ae));
+  g_assert_cmpint (4, ==, archive_write_data (a, "bar\n", 4));
+  archive_entry_free (ae);
+
+  ae = archive_entry_new ();
+  archive_entry_set_pathname (ae, "/etc");
+  archive_entry_set_mode (ae, S_IFDIR | 0755);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
+  g_assert_cmpint (0, ==, archive_write_header (a, ae));
+  archive_entry_free (ae);
+
+  ae = archive_entry_new ();
+  archive_entry_set_pathname (ae, "/etc/file");
+  archive_entry_set_mode (ae, S_IFREG | 0777);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
   archive_entry_set_size (ae, 4);
   g_assert_cmpint (0, ==, archive_write_header (a, ae));
   g_assert_cmpint (4, ==, archive_write_data (a, "bar\n", 4));
@@ -123,6 +151,15 @@ spawn_cmdline (const char *cmd, GError **error)
   return TRUE;
 }
 
+static void
+test_archive_setup (int fd, struct archive *a)
+{
+  g_assert_cmpint (0, ==, lseek (fd, 0, SEEK_SET));
+  g_assert_cmpint (0, ==, archive_read_support_format_all (a));
+  g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
+  g_assert_cmpint (0, ==, archive_read_open_fd (a, fd, 8192));
+}
+
 static void
 test_libarchive_noautocreate_empty (gconstpointer data)
 {
@@ -132,10 +169,7 @@ test_libarchive_noautocreate_empty (gconstpointer data)
   OstreeRepoImportArchiveOptions opts = { 0, };
   glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
 
-  g_assert_cmpint (0, ==, lseek (td->fd_empty, 0, SEEK_SET));
-  g_assert_cmpint (0, ==, archive_read_support_format_all (a));
-  g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
-  g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd_empty, 8192));
+  test_archive_setup (td->fd_empty, a);
 
   (void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error);
   g_assert_no_error (error);
@@ -153,10 +187,7 @@ test_libarchive_autocreate_empty (gconstpointer data)
 
   opts.autocreate_parents = 1;
 
-  g_assert_cmpint (0, ==, lseek (td->fd_empty, 0, SEEK_SET));
-  g_assert_cmpint (0, ==, archive_read_support_format_all (a));
-  g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
-  g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd_empty, 8192));
+  test_archive_setup (td->fd_empty, a);
 
   (void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error);
   g_assert_no_error (error);
@@ -172,68 +203,95 @@ test_libarchive_error_device_file (gconstpointer data)
   OstreeRepoImportArchiveOptions opts = { 0, };
   glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
 
-  g_assert_cmpint (0, ==, lseek (td->fd, 0, SEEK_SET));
-  g_assert_cmpint (0, ==, archive_read_support_format_all (a));
-  g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
-  g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd, 8192));
+  test_archive_setup (td->fd, a);
 
   (void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error);
   g_assert (error != NULL);
 }
 
-static void
-test_libarchive_ignore_device_file (gconstpointer data)
+static gboolean
+skip_if_no_xattr (TestData *td)
 {
-  TestData *td = (void*)data;
-  GError *error = NULL;
-  GCancellable *cancellable = NULL;
-  struct archive *a = archive_read_new ();
-  OstreeRepoImportArchiveOptions opts = { 0, };
-  glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
-  glnx_unref_object GFile *root = NULL;
-  g_autofree char *commit_checksum = NULL;
-
+  /* /var/tmp might actually be a tmpfs */
   if (setxattr (td->tmpd, "user.test-xattr-support", "yes", 4, 0) != 0)
     {
       int saved_errno = errno;
-      g_autofree gchar *message = g_strdup_printf ("unable to setxattr on \"%s\": %s", td->tmpd, g_strerror (saved_errno));
-
+      g_autofree gchar *message
+        = g_strdup_printf ("unable to setxattr on \"%s\": %s",
+                           td->tmpd, g_strerror (saved_errno));
       g_test_skip (message);
-      goto out;
+      return TRUE;
     }
 
-  g_assert_cmpint (0, ==, lseek (td->fd, 0, SEEK_SET));
-  g_assert_cmpint (0, ==, archive_read_support_format_all (a));
-  g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
-  g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd, 8192));
+  return FALSE;
+}
 
-  opts.ignore_unsupported_content = TRUE;
+static gboolean
+import_write_and_ref (OstreeRepo      *repo,
+                      OstreeRepoImportArchiveOptions *opts,
+                      struct archive  *a,
+                      const char      *ref,
+                      OstreeRepoCommitModifier *modifier,
+                      GError         **error)
+{
+  gboolean ret = FALSE;
+  glnx_unref_object GFile *root = NULL;
+  g_autofree char *commit_checksum = NULL;
+  glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
 
-  if (!ostree_repo_prepare_transaction (td->repo, NULL, cancellable, &error))
+  if (!ostree_repo_prepare_transaction (repo, NULL, NULL, error))
     goto out;
 
-  if (!ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error))
+  if (!ostree_repo_import_archive_to_mtree (repo, opts, a, mtree, modifier,
+                                            NULL, error))
     goto out;
 
-  if (!ostree_repo_write_mtree (td->repo, mtree, &root, cancellable, &error))
+  if (!ostree_repo_write_mtree (repo, mtree, &root, NULL, error))
     goto out;
 
-  if (!ostree_repo_write_commit (td->repo, NULL, "", "", NULL,
+  if (!ostree_repo_write_commit (repo, NULL, "", "", NULL,
                                  OSTREE_REPO_FILE (root),
-                                 &commit_checksum, cancellable, &error))
+                                 &commit_checksum, NULL, error))
     goto out;
 
-  ostree_repo_transaction_set_ref (td->repo, NULL, "foo", commit_checksum);
-  
-  if (!ostree_repo_commit_transaction (td->repo, NULL, cancellable, &error))
+  ostree_repo_transaction_set_ref (repo, NULL, ref, commit_checksum);
+
+  if (!ostree_repo_commit_transaction (repo, NULL, NULL, error))
+    goto out;
+
+  ret = TRUE;
+out:
+  return ret;
+}
+
+static void
+test_libarchive_ignore_device_file (gconstpointer data)
+{
+  TestData *td = (void*)data;
+  GError *error = NULL;
+  struct archive *a = archive_read_new ();
+  OstreeRepoImportArchiveOptions opts = { 0, };
+
+  if (skip_if_no_xattr (td))
+    goto out;
+
+  test_archive_setup (td->fd, a);
+
+  opts.ignore_unsupported_content = TRUE;
+
+  if (!import_write_and_ref (td->repo, &opts, a, "foo", NULL, &error))
     goto out;
 
+  /* check contents */
   if (!spawn_cmdline ("ostree --repo=repo ls foo file", &error))
     goto out;
 
   if (!spawn_cmdline ("ostree --repo=repo ls foo anotherfile", &error))
     goto out;
 
+  if (!spawn_cmdline ("ostree --repo=repo ls foo /etc/file", &error))
+    goto out;
+
   if (spawn_cmdline ("ostree --repo=repo ls foo devnull", &error))
     g_assert_not_reached ();
   g_assert (error != NULL);
@@ -243,6 +301,243 @@ test_libarchive_ignore_device_file (gconstpointer data)
   g_assert_no_error (error);
 }
 
+static gboolean
+check_ostree_convention (GError *error)
+{
+  if (!spawn_cmdline ("ostree --repo=repo ls bar file", &error))
+    return FALSE;
+
+  if (!spawn_cmdline ("ostree --repo=repo ls bar anotherfile", &error))
+    return FALSE;
+
+  if (!spawn_cmdline ("ostree --repo=repo ls bar /usr/etc/file", &error))
+    return FALSE;
+
+  if (spawn_cmdline ("ostree --repo=repo ls bar /etc/file", &error))
+    g_assert_not_reached ();
+  g_assert (error != NULL);
+  g_clear_error (&error);
+
+  if (spawn_cmdline ("ostree --repo=repo ls bar devnull", &error))
+    g_assert_not_reached ();
+  g_assert (error != NULL);
+  g_clear_error (&error);
+
+  return TRUE;
+}
+
+static void
+test_libarchive_ostree_convention (gconstpointer data)
+{
+  TestData *td = (void*)data;
+  GError *error = NULL;
+  struct archive *a = archive_read_new ();
+  OstreeRepoImportArchiveOptions opts = { 0, };
+
+  if (skip_if_no_xattr (td))
+    goto out;
+
+  test_archive_setup (td->fd, a);
+
+  opts.autocreate_parents = TRUE;
+  opts.use_ostree_convention = TRUE;
+  opts.ignore_unsupported_content = TRUE;
+
+  if (!import_write_and_ref (td->repo, &opts, a, "bar", NULL, &error))
+    goto out;
+
+  if (!check_ostree_convention (error))
+    goto out;
+
+ out:
+  g_assert_no_error (error);
+}
+
+static GVariant*
+xattr_cb (OstreeRepo  *repo,
+          const char  *path,
+          GFileInfo   *file_info,
+          gpointer     user_data)
+{
+  g_auto(GVariantBuilder) builder;
+  g_variant_builder_init (&builder, (GVariantType*)"a(ayay)");
+  if (strcmp (path, "/anotherfile") == 0)
+    g_variant_builder_add (&builder, "(@ay@ay)",
+                           g_variant_new_bytestring ("user.data"),
+                           g_variant_new_bytestring ("mydata"));
+  return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+static void
+test_libarchive_xattr_callback (gconstpointer data)
+{
+  TestData *td = (void*)data;
+  GError *error = NULL;
+  struct archive *a = archive_read_new ();
+  OstreeRepoImportArchiveOptions opts = { 0 };
+  OstreeRepoCommitModifier *modifier = NULL;
+  char buf[7] = { 0 };
+
+  if (skip_if_no_xattr (td))
+    goto out;
+
+  modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
+  ostree_repo_commit_modifier_set_xattr_callback (modifier, xattr_cb,
+                                                  NULL, NULL);
+
+  test_archive_setup (td->fd, a);
+
+  opts.ignore_unsupported_content = TRUE;
+
+  if (!import_write_and_ref (td->repo, &opts, a, "baz", modifier, &error))
+    goto out;
+
+  /* check contents */
+  if (!spawn_cmdline ("ostree --repo=repo checkout baz baz-checkout", &error))
+    goto out;
+
+  g_assert_cmpint (0, >, getxattr ("baz-checkout/file", "user.data", NULL, 0));
+  g_assert_cmpint (ENODATA, ==, errno);
+
+  if (getxattr ("baz-checkout/anotherfile", "user.data", buf, sizeof buf) < 0)
+    {
+      glnx_set_prefix_error_from_errno (&error, "%s", "getxattr");
+      goto out;
+    }
+
+  g_assert_cmpstr (buf, ==, "mydata");
+
+ out:
+  if (modifier)
+    ostree_repo_commit_modifier_unref (modifier);
+  g_assert_no_error (error);
+}
+
+static GVariant*
+path_cb (OstreeRepo  *repo,
+         const char  *path,
+         GFileInfo   *file_info,
+         gpointer     user_data)
+{
+  if (strcmp (path, "/etc/file") == 0)
+    *(gboolean*)user_data = TRUE;
+  return NULL;
+}
+
+static void
+entry_pathname_test_helper (gconstpointer data, gboolean on)
+{
+  TestData *td = (void*)data; GError *error = NULL;
+  struct archive *a = archive_read_new ();
+  OstreeRepoImportArchiveOptions opts = { 0, };
+  OstreeRepoCommitModifier *modifier = NULL;
+  gboolean met_etc_file = FALSE;
+
+  modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
+  ostree_repo_commit_modifier_set_xattr_callback (modifier, path_cb,
+                                                  NULL, &met_etc_file);
+
+  test_archive_setup (td->fd, a);
+
+  opts.autocreate_parents = TRUE;
+  opts.use_ostree_convention = TRUE;
+  opts.ignore_unsupported_content = TRUE;
+  opts.callback_with_entry_pathname = on;
+
+  if (!import_write_and_ref (td->repo, &opts, a, "bar", modifier, &error))
+    goto out;
+
+  /* the flag shouldn't have any effect on the final tree */
+  if (!check_ostree_convention (error))
+    goto out;
+
+  if (!on && met_etc_file)
+    {
+      g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Received callback with /etc/file");
+      goto out;
+    }
+
+  if (on && !met_etc_file)
+    {
+      g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Did not receive callback with /etc/file");
+      goto out;
+    }
+
+ out:
+  g_assert_no_error (error);
+}
+
+static void
+test_libarchive_no_use_entry_pathname (gconstpointer data)
+{
+  entry_pathname_test_helper (data, FALSE);
+}
+
+static void
+test_libarchive_use_entry_pathname (gconstpointer data)
+{
+  entry_pathname_test_helper (data, TRUE);
+}
+
+static void
+test_libarchive_selinux (gconstpointer data)
+{
+  TestData *td = (void*)data;
+  GError *error = NULL;
+  struct archive *a = archive_read_new ();
+  OstreeRepoImportArchiveOptions opts = { 0 };
+  glnx_unref_object OstreeSePolicy *sepol = NULL;
+  OstreeRepoCommitModifier *modifier = NULL;
+  char buf[64] = { 0 };
+
+  if (skip_if_no_xattr (td))
+    goto out;
+
+  {
+    glnx_unref_object GFile *root = g_file_new_for_path ("/");
+
+    /* creation should always succeed */
+    sepol = ostree_sepolicy_new (root, NULL, &error);
+    g_assert (sepol != NULL);
+  }
+
+  if (ostree_sepolicy_get_name (sepol) == NULL)
+    {
+      g_test_skip ("SELinux disabled");
+      goto out;
+    }
+
+  modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
+  ostree_repo_commit_modifier_set_sepolicy (modifier, sepol);
+
+  test_archive_setup (td->fd, a);
+
+  opts.ignore_unsupported_content = TRUE;
+
+  if (!import_write_and_ref (td->repo, &opts, a, "bob", modifier, &error))
+    goto out;
+
+  /* check contents */
+  if (!spawn_cmdline ("ostree --repo=repo checkout bob bob-checkout", &error))
+    goto out;
+
+  if (getxattr ("bob-checkout/etc", "security.selinux", buf, sizeof buf) < 0)
+    {
+      glnx_set_prefix_error_from_errno (&error, "%s", "getxattr");
+      goto out;
+    }
+
+  buf[(sizeof buf) - 1] = '\0';
+  g_assert_cmpstr (buf, ==, "system_u:object_r:etc_t:s0");
+
+ out:
+  if (modifier)
+    ostree_repo_commit_modifier_unref (modifier);
+  g_assert_no_error (error);
+}
+
 int main (int argc, char **argv)
 {
   TestData td = {NULL,};
@@ -256,10 +551,15 @@ int main (int argc, char **argv)
   g_test_add_data_func ("/libarchive/autocreate-empty", &td, test_libarchive_autocreate_empty);
   g_test_add_data_func ("/libarchive/error-device-file", &td, test_libarchive_error_device_file);
   g_test_add_data_func ("/libarchive/ignore-device-file", &td, test_libarchive_ignore_device_file);
+  g_test_add_data_func ("/libarchive/ostree-convention", &td, test_libarchive_ostree_convention);
+  g_test_add_data_func ("/libarchive/xattr-callback", &td, test_libarchive_xattr_callback);
+  g_test_add_data_func ("/libarchive/no-use-entry-pathname", &td, test_libarchive_no_use_entry_pathname);
+  g_test_add_data_func ("/libarchive/use-entry-pathname", &td, test_libarchive_use_entry_pathname);
+  g_test_add_data_func ("/libarchive/selinux", &td, test_libarchive_selinux);
 
   r = g_test_run();
 
-  if (td.tmpd)
+  if (td.tmpd && g_getenv ("TEST_SKIP_CLEANUP") == NULL)
     (void) glnx_shutil_rm_rf_at (AT_FDCWD, td.tmpd, NULL, NULL);
   return r;
 }
index 7309ffd2c177e11b7ece404e37c37600266cf4e2..0c579459526129813252788314b18ceefbfbdc21 100755 (executable)
@@ -26,57 +26,95 @@ fi
 
 . $(dirname $0)/libtest.sh
 
-echo "1..7"
+echo "1..20"
 
 setup_test_repository "bare"
+
 cd ${test_tmpdir}
 mkdir foo
 cd foo
-echo hi > hi
-ln -s hi hello
-mkdir subdir
-echo contents > subdir/more
-mkdir subdir/1
-touch subdir/1/empty
-mkdir subdir/2
-touch subdir/2/empty
-echo not > subdir/2/notempty
+mkdir -p usr/bin
+echo contents > usr/bin/foo
+touch usr/bin/foo0
+ln usr/bin/foo usr/bin/bar
+ln usr/bin/foo0 usr/bin/bar0
+ln -s foo usr/bin/sl
+mkdir -p usr/local/bin
+ln usr/bin/foo usr/local/bin/baz
+ln usr/bin/foo0 usr/local/bin/baz0
+ln usr/bin/sl usr/local/bin/slhl
+touch usr/bin/setuidme
+touch usr/bin/skipme
 
 tar -c -z -f ../foo.tar.gz .
+find . | cpio -o -H newc > ../foo.cpio
+
 cd ..
-$OSTREE commit -s "from tar" -b test-tar --tree=tar=foo.tar.gz
+
+cat > statoverride.txt <<EOF
+2048 /usr/bin/setuidme
+EOF
+
+cat > skiplist.txt <<EOF
+/usr/bin/skipme
+EOF
+
+$OSTREE commit -s "from tar" -b test-tar \
+  --statoverride=statoverride.txt \
+  --skip-list=skiplist.txt \
+  --tree=tar=foo.tar.gz
 echo "ok tar commit"
+$OSTREE commit -s "from cpio" -b test-cpio \
+  --statoverride=statoverride.txt \
+  --skip-list=skiplist.txt \
+  --tree=tar=foo.cpio
+echo "ok cpio commit"
 
-cd ${test_tmpdir}
-$OSTREE checkout test-tar test-tar-checkout
-cd test-tar-checkout
-assert_file_has_content hi hi
-assert_file_has_content hello hi
-assert_file_has_content subdir/more contents
-assert_has_file subdir/1/empty
-assert_has_file subdir/2/empty
-cd ${test_tmpdir}
-rm -rf test-tar-checkout
-echo "ok tar contents"
+assert_valid_checkout () {
+  cd ${test_tmpdir}
+  $OSTREE checkout test-$1 test-$1-checkout
+  cd test-$1-checkout
 
-cd ${test_tmpdir}
-mkdir hardlinktest
-cd hardlinktest
-echo other > otherfile
-echo foo1 > foo
-ln foo bar
-tar czf ${test_tmpdir}/hardlinktest.tar.gz .
-cd ${test_tmpdir}
-$OSTREE commit -s 'hardlinks' -b test-hardlinks --tree=tar=hardlinktest.tar.gz
-rm -rf hardlinktest
-echo "ok hardlink commit"
+  # basic content check
+  assert_file_has_content usr/bin/foo contents
+  assert_file_has_content usr/bin/bar contents
+  assert_file_has_content usr/local/bin/baz contents
+  assert_file_empty usr/bin/foo0
+  assert_file_empty usr/bin/bar0
+  assert_file_empty usr/local/bin/baz0
+  echo "ok $1 contents"
 
-cd ${test_tmpdir}
-$OSTREE checkout test-hardlinks test-hardlinks-checkout
-cd test-hardlinks-checkout
-assert_file_has_content foo foo1
-assert_file_has_content bar foo1
-echo "ok hardlink contents"
+  # hardlinks
+  assert_files_hardlinked usr/bin/foo usr/bin/bar
+  assert_files_hardlinked usr/bin/foo usr/local/bin/baz
+  echo "ok $1 hardlink"
+  assert_files_hardlinked usr/bin/foo0 usr/bin/bar0
+  assert_files_hardlinked usr/bin/foo0 usr/local/bin/baz0
+  echo "ok $1 hardlink to empty files"
+
+  # symlinks
+  assert_symlink_has_content usr/bin/sl foo
+  assert_file_has_content usr/bin/sl contents
+  echo "ok $1 symlink"
+  # ostree checkout doesn't care if two symlinks are actually hardlinked
+  # together (which is fine). checking that it's also a symlink is good enough.
+  assert_symlink_has_content usr/local/bin/slhl foo
+  echo "ok $1 hardlink to symlink"
+
+  # stat override
+  test -u usr/bin/setuidme
+  echo "ok $1 setuid"
+
+  # skip list
+  test ! -f usr/bin/skipme
+  echo "ok $1 file skip"
+
+  cd ${test_tmpdir}
+  rm -rf test-$1-checkout
+}
+
+assert_valid_checkout tar
+assert_valid_checkout cpio
 
 cd ${test_tmpdir}
 mkdir multicommit-files
@@ -115,3 +153,4 @@ cd ${test_tmpdir}
 $OSTREE checkout partial partial-checkout
 cd partial-checkout
 assert_file_has_content subdir/original "original"
+echo "ok tar partial commit contents"
index 05b102a20eebc88171e44e5efbe5014fdea99071..d1f5d7c97959460851fe8488daade099f909d4c0 100755 (executable)
@@ -21,6 +21,8 @@ set -euo pipefail
 
 . $(dirname $0)/libtest.sh
 
+skip_without_user_xattrs
+
 echo '1..2'
 
 setup_test_repository "archive-z2"
diff --git a/tests/test-pull-c.c b/tests/test-pull-c.c
new file mode 100644 (file)
index 0000000..d784312
--- /dev/null
@@ -0,0 +1,132 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "libglnx.h"
+#include <glib.h>
+#include <stdlib.h>
+#include <gio/gio.h>
+#include <string.h>
+
+#include "libostreetest.h"
+
+typedef struct {
+  OstreeRepo *repo;
+} TestData;
+
+static void
+test_data_init (TestData *td)
+{
+  GError *local_error = NULL;
+  GError **error = &local_error;
+  g_autofree char *http_address = NULL;
+  g_autofree char *repo_url = NULL;
+
+  td->repo = ot_test_setup_repo (NULL, error);
+  if (!td->repo)
+    goto out;
+
+  if (!ot_test_run_libtest ("setup_fake_remote_repo1 archive-z2", error))
+    goto out;
+
+  if (!g_file_get_contents ("httpd-address", &http_address, NULL, error))
+    goto out;
+
+  repo_url = g_strconcat (http_address, "/ostree/gnomerepo", NULL);
+
+  { g_autoptr(GVariantBuilder) builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+    g_autoptr(GVariant) opts = NULL;
+
+    g_variant_builder_add (builder, "{s@v}", "gpg-verify", g_variant_new_variant (g_variant_new_boolean (FALSE)));
+    opts = g_variant_ref_sink (g_variant_builder_end (builder));
+
+    if (!ostree_repo_remote_change (td->repo, NULL, OSTREE_REPO_REMOTE_CHANGE_ADD,
+                                    "origin", repo_url, opts, NULL, error))
+      goto out;
+  }
+
+ out:
+  g_assert_no_error (local_error);
+}
+
+static void
+test_pull_multi_nochange (gconstpointer data)
+{
+  GError *local_error = NULL;
+  GError **error = &local_error;
+  TestData *td = (void*)data;
+  char *refs[] = { "main", NULL };
+
+  if (!ostree_repo_pull (td->repo, "origin", (char**)&refs, 0, NULL, NULL, error))
+    goto out;
+  if (!ostree_repo_pull (td->repo, "origin", (char**)&refs, 0, NULL, NULL, error))
+    goto out;
+  if (!ostree_repo_pull (td->repo, "origin", (char**)&refs, 0, NULL, NULL, error))
+    goto out;
+  
+ out:
+  g_assert_no_error (local_error);
+}
+
+static void
+test_pull_multi_error_then_ok (gconstpointer data)
+{
+  GError *local_error = NULL;
+  GError **error = &local_error;
+  
+  TestData *td = (void*)data;
+  char *ok_refs[] = { "main", NULL };
+  char *bad_refs[] = { "nosuchbranch", NULL };
+
+  for (guint i = 0; i < 3; i++)
+    {
+      g_autoptr(GError) tmp_error = NULL;
+      if (!ostree_repo_pull (td->repo, "origin", (char**)&ok_refs, 0, NULL, NULL, error))
+        goto out;
+      if (ostree_repo_pull (td->repo, "origin", (char**)&bad_refs, 0, NULL, NULL, &tmp_error))
+        g_assert_not_reached ();
+      g_clear_error (&tmp_error);
+      if (ostree_repo_pull (td->repo, "origin", (char**)&bad_refs, 0, NULL, NULL, &tmp_error))
+        g_assert_not_reached ();
+      g_clear_error (&tmp_error);
+      if (!ostree_repo_pull (td->repo, "origin", (char**)&ok_refs, 0, NULL, NULL, error))
+        goto out;
+    }
+  
+ out:
+  g_assert_no_error (local_error);
+}
+
+int main (int argc, char **argv)
+{
+  TestData td = {NULL,};
+  int r;
+
+  test_data_init (&td);
+
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_data_func ("/test-pull-c/multi-nochange", &td, test_pull_multi_nochange);
+  g_test_add_data_func ("/test-pull-c/multi-ok-error-repeat", &td, test_pull_multi_error_then_ok);
+
+  r = g_test_run();
+
+  return r;
+}
diff --git a/tests/test-pull-override-url.sh b/tests/test-pull-override-url.sh
new file mode 100755 (executable)
index 0000000..d81b345
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 Endless Mobile, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+set -euo pipefail
+
+. $(dirname $0)/libtest.sh
+
+setup_fake_remote_repo1 "archive-z2"
+
+echo '1..1'
+
+cd ${test_tmpdir}
+# get a list of XX/XXXXXXX...XX.commit
+find ostree-srv/gnomerepo/objects -name '*.commit' | cut -d/ -f4- | sort >${test_tmpdir}/original_commits
+
+
+gnomerepo_url="$(cat httpd-address)/ostree/gnomerepo"
+mkdir mirror-srv
+cd mirror-srv
+mkdir gnomerepo
+${CMD_PREFIX} ostree --repo=gnomerepo init --mode "archive-z2"
+${CMD_PREFIX} ostree --repo=gnomerepo remote add --set=gpg-verify=false origin ${gnomerepo_url}
+${CMD_PREFIX} ostree --repo=gnomerepo pull --mirror --depth=-1 origin main
+
+find gnomerepo/objects -name '*.commit' | cut -d/ -f3- | sort >${test_tmpdir}/mirror_commits
+
+if ! cmp --quiet ${test_tmpdir}/original_commits ${test_tmpdir}/mirror_commits
+then
+    assert_not_reached 'original repository and its mirror should have the same commits'
+fi
+
+cd ${test_tmpdir}
+mkdir mirror-httpd
+cd mirror-httpd
+ln -s ${test_tmpdir}/mirror-srv ostree
+mirror_log="${test_tmpdir}/mirror_log"
+${CMD_PREFIX} ostree trivial-httpd --log-file=${mirror_log} --autoexit --daemonize -p ${test_tmpdir}/mirror-httpd-port
+port=$(cat ${test_tmpdir}/mirror-httpd-port)
+echo "http://127.0.0.1:${port}" > ${test_tmpdir}/mirror-httpd-address
+
+cd ${test_tmpdir}
+mirrorrepo_url="$(cat mirror-httpd-address)/ostree/gnomerepo"
+mkdir repo
+${CMD_PREFIX} ostree --repo=repo init
+${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin ${gnomerepo_url}
+rm -rf ${test_tmpdir}/ostree-srv/gnomerepo
+if ${CMD_PREFIX} ostree --repo=repo pull --depth=-1 origin main
+then
+    assert_not_reached 'pulling from removed remote should have failed'
+fi
+
+if ! ${CMD_PREFIX} ostree --repo=repo pull --depth=-1 --url=${mirrorrepo_url} origin main
+then
+    cat ${mirror_log}
+    assert_not_reached 'fetching from the overridden URL should succeed'
+fi
+find repo/objects -name '*.commit' | cut -d/ -f3- | sort >${test_tmpdir}/repo_commits
+
+if ! cmp --quiet ${test_tmpdir}/original_commits ${test_tmpdir}/repo_commits
+then
+    assert_not_reached 'original repository and its mirror should have the same commits'
+fi
+
+echo "ok pull url override"
index bcebae9edffe9e05d4c79161a3a623493c35efd7..3d2290316a47638a2504158e6c24cbfb0ca355dc 100755 (executable)
@@ -65,4 +65,35 @@ ${CMD_PREFIX} ostree refs --repo=repo | wc -l > refscount.delete3
 assert_file_has_content refscount.delete3 "^3$"
 assert_not_file_has_content reflist '^test-1$'
 
+#Add a few more commits, to test --create
+${CMD_PREFIX} ostree --repo=repo commit --branch=ctest -m ctest -s ctest tree
+${CMD_PREFIX} ostree --repo=repo commit --branch=foo/ctest -m ctest -s ctest tree
+
+${CMD_PREFIX} ostree --repo=repo refs | wc -l > refscount
+assert_file_has_content refscount "^5$"
+
+if ${CMD_PREFIX} ostree --repo=repo refs --create=ctest-new; then
+    assert_not_reached "refs --create unexpectedly succeeded without specifying an existing ref!"
+fi
+if ${CMD_PREFIX} ostree --repo=repo refs ctest --create; then
+    assert_not_reached "refs --create unexpectedly succeeded without specifying the ref to create!"
+fi
+if ${CMD_PREFIX} ostree --repo=repo refs does-not-exist --create=ctest-new; then
+    assert_not_reached "refs --create unexpectedly succeeded for a prefix that doesn't exist!"
+fi
+if ${CMD_PREFIX} ostree --repo=repo refs ctest --create=foo; then
+    assert_not_reached "refs --create unexpectedly succeeded for a prefix that is already in use by a folder!"
+fi
+if ${CMD_PREFIX} ostree --repo=repo refs foo/ctest --create=ctest; then
+    assert_not_reached "refs --create unexpectedly succeeded in overwriting an existing prefix!"
+fi
+
+#Check to see if any uncleaned tmp files were created after failed --create
+${CMD_PREFIX} ostree --repo=repo refs | wc -l > refscount.create1
+assert_file_has_content refscount.create1 "^5$"
+
+${CMD_PREFIX} ostree --repo=repo refs ctest --create ctest-new
+${CMD_PREFIX} ostree --repo=repo refs | wc -l > refscount.create2
+assert_file_has_content refscount.create2 "^6$"
+
 echo "ok refs"
diff --git a/tests/test-setuid.sh b/tests/test-setuid.sh
deleted file mode 100755 (executable)
index edf707d..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2013 Colin Walters <walters@verbum.org>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-set -euo pipefail
-
-echo "1..1"
-
-. $(dirname $0)/libtest.sh
-
-setup_test_repository "bare"
-
-cd ${test_tmpdir}
-cat > test-statoverride.txt <<EOF
-+2048 /abinary
-EOF
-$OSTREE checkout test2 test2-checkout
-touch test2-checkout/abinary
-chmod a+x test2-checkout/abinary
-(cd test2-checkout && $OSTREE commit -b test2 -s "with statoverride" --statoverride=../test-statoverride.txt)
-rm -rf test2-checkout
-$OSTREE checkout test2 test2-checkout
-test -u test2-checkout/abinary
-
-echo "ok"
diff --git a/tests/test-symbols.sh b/tests/test-symbols.sh
new file mode 100755 (executable)
index 0000000..55157c0
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 Colin Walters <walters@verbum.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+set -euo pipefail
+
+echo '1..2'
+
+echo "Verifying all expected symbols are actually exported..."
+grep ' ostree_[A-Za-z0-9_]*;' ${G_TEST_SRCDIR}/src/libostree/libostree.sym | sed -e 's,^ *\([A-Za-z0-9_]*\);,\1,' | sort -u > expected-symbols.txt
+eu-readelf -a ${G_TEST_BUILDDIR}/.libs/libostree-1.so | grep 'FUNC.*GLOBAL.*DEFAULT.*@@LIBOSTREE_' | sed -e 's,^.* \(ostree_[A-Za-z0-9_]*\)@@LIBOSTREE_[0-9_.]*,\1,' |sort -u > found-symbols.txt
+diff -u expected-symbols.txt found-symbols.txt
+echo "ok exports"
+
+# cmd__private__ is private.  The fetcher symbol should not have been made public.
+grep -E -v '(ostree_cmd__private__)|(ostree_fetcher_config_flags_get_type)' found-symbols.txt > expected-documented.txt
+
+echo "Verifying all public symbols are documented:"
+grep '^ostree_' ${G_TEST_SRCDIR}/apidoc/ostree-sections.txt |sort -u > found-documented.txt
+diff -u expected-documented.txt found-documented.txt
+
+echo 'ok documented symbols'
index 9468d2fb8f1cca82f71692f77343b97e3c500fce..7c31659dfb8cae36e17447cd5347e32aa5821c3c 100644 (file)
@@ -37,7 +37,7 @@ function libtestExec(shellCode) {
     let testdatadir = GLib.getenv("G_TEST_SRCDIR");
     let libtestPath = GLib.build_filenamev([testdatadir, 'libtest.sh'])
     let proc = GSystem.Subprocess.new_simple_argv(['bash', '-c',
-                                                  '. ' + GLib.shell_quote(libtestPath) + '; ' + shellCode],
+                                                  'set -xeuo pipefail; . ' + GLib.shell_quote(libtestPath) + '; ' + shellCode],
                                                  GSystem.SubprocessStreamDisposition.INHERIT,
                                                  GSystem.SubprocessStreamDisposition.INHERIT,
                                                  null);